.NET之生成數據庫全流程

開篇語

本文主要是回顧下從項目創建到生成數據到數據庫(代碼優先)的全部過程。採用EFCore作爲ORM框架。

本次示例環境:vs2019、net5、mysql

創建項目

本次事例代碼是用過vs2019創建的ASP.NET Core Web API項目

可以通過可視化界面創建或者通過命令行創建

dotnet new webapi -o Net5ByDocker

創建實體類

安裝組件

    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0" />
    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Json.Newtonsoft" Version="5.0.0" />

增加實體類

    [Table("user")]
    public class User
    {
        public User()
        {
            Id = Guid.NewGuid().ToString();
        }

        public User(string account, string password, string creater) : this()
        {
            Account = account;
            Password = password;
            Deleted = false;
            SetCreater(creater);
        }

        [Key]
        [Comment("主鍵")]
        [StringLength(36)]
        [Required]
        public string Id { get; private set; }

        [Comment("帳號")]
        [StringLength(36)]
        [Required]
        public string Account { get; private set; }

        [Comment("密碼")]
        [StringLength(36)]
        [Required]
        public string Password { get; private set; }

        [Comment("餘額")]
        [Column(TypeName = "decimal(18, 2)")]
        [Required]
        public decimal Money { get; set; }

        [Comment("是否刪除")]
        [Column(TypeName = "tinyint(1)")]
        [Required]
        public bool Deleted { get; private set; }

        [Comment("創建人")]
        [StringLength(20)]
        [Required]
        public string Creater { get; private set; }

        [Comment("創建時間")]
        [Required]
        public DateTime CreateTime { get; private set; }

        [Comment("修改人")]
        [StringLength(20)]
        [Required]
        public string Modifyer { get; private set; }

        [Comment("修改時間")]
        [Required]
        public DateTime ModifyTime { get; private set; }

        public void SetCreater(string name)
        {
            Creater = name;
            CreateTime = DateTime.Now;
            SetModifyer(name);
        }

        public void SetModifyer(string name)
        {
            Modifyer = name;
            ModifyTime = DateTime.Now;
        }
    }

這種只是增加實體類類型的一種方式,可能這種看着比較亂,還可以通過OnModelCreating實現,詳情看參考文檔

增加數據庫上下文OpenDbContext

    public class OpenDbContext : DbContext
    {
        public OpenDbContext(DbContextOptions<OpenDbContext> options)
            : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
    }

Startup注入連接數據庫操作

            var connection = Configuration["DbConfig:Mysql:ConnectionString"];
            var migrationsAssembly = IntrospectionExtensions.GetTypeInfo(typeof(Startup)).Assembly.GetName().Name;
            services.AddDbContext<OpenDbContext>(option => option.UseMySql(connection, ServerVersion.AutoDetect(connection), x =>
            {
                x.UseNewtonsoftJson();
                x.MigrationsAssembly(migrationsAssembly);
            }));

生成遷移文件

引用組件

<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.5">

遷移命令

add-migration Init

結果

image.png
image.png

要看下生成的遷移文件是否是自己預期的那樣子,也可以在這一步就生成數據庫,命令:Update-Database

數據種子

增加OpenDbSend類,添加數據種子

    public class OpenDbSend
    {
        /// <summary>
        /// 生成數據庫以及數據種子
        /// </summary>
        /// <param name="dbContext">數據庫上下文</param>
        /// <param name="loggerFactory">日誌</param>
        /// <param name="retry">重試次數</param>
        /// <returns></returns>
        public static async Task SeedAsync(OpenDbContext dbContext,
            ILoggerFactory loggerFactory,
            int? retry = 0)
        {
            int retryForAvailability = retry.Value;
            try
            {
                dbContext.Database.Migrate();//如果當前數據庫不存在按照當前 model 創建,如果存在則將數據庫調整到和當前 model 匹配
                await InitializeAsync(dbContext).ConfigureAwait(false);

                //if (dbContext.Database.EnsureCreated())//如果當前數據庫不存在按照當前 model創建,如果存在則不管了。
                //  await InitializeAsync(dbContext).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                if (retryForAvailability < 3)
                {
                    retryForAvailability++;
                    var log = loggerFactory.CreateLogger<OpenDbSend>();
                    log.LogError(ex.Message);
                    await SeedAsync(dbContext, loggerFactory, retryForAvailability).ConfigureAwait(false);
                }
            }
        }

        /// <summary>
        /// 初始化數據
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static async Task InitializeAsync(OpenDbContext context)
        {
            if (!context.Set<User>().Any())
            {
                await context.Set<User>().AddAsync(new User("azrng""123456""azrng")).ConfigureAwait(false);
                await context.Set<User>().AddAsync(new User("張三""123456""azrng")).ConfigureAwait(false);
            }
            await context.SaveChangesAsync().ConfigureAwait(false);
        }
    }

設置項目啓動時候調用

        public static async Task Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                var loggerFactory = services.GetRequiredService<ILoggerFactory>();
                var _logger = loggerFactory.CreateLogger<Program>();
                try
                {
                    var openContext = services.GetRequiredService<OpenDbContext>();
                    await OpenDbSend.SeedAsync(openContext, loggerFactory).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"項目啓動出錯  {ex.Message}");
                }
            }

            await host.RunAsync().ConfigureAwait(false);
        }

生成數據庫

啓動項目,自動生成數據庫

image.png
image.png

表結構如下

image.png
image.png

如果後期數據庫字段或者結構有變動,可以再次生成遷移文件然後生成數據庫

查詢數據

    /// <summary>
    /// 用戶接口
    /// </summary>
    public interface IUserService
    {
        string GetName();

        /// <summary>
        /// 查詢用戶信息
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        Task<User> GetDetailsAsync(string account);
    }

    /// <summary>
    /// 用戶實現
    /// </summary>
    public class UserService : IUserService
    {
        private readonly OpenDbContext _dbContext;

        public UserService(OpenDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public string GetName()
        {
            return "AZRNG";
        }

        ///<inheritdoc cref="IUserService.GetDetailsAsync(string)"/>
        public async Task<User> GetDetailsAsync(string account)
        {
            return await _dbContext.Set<User>().FirstOrDefaultAsync(t => t.Account == account).ConfigureAwait(false);
        }
    }

一般更推薦建立指定的返回Model類,然後只查詢需要的內容,不直接返回實體類

控制器方法

        /// <summary>
        /// 查詢用戶詳情
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<ActionResult<User>> GetDetailsAsync(string account)
        {
            return await _userService.GetDetailsAsync(account).ConfigureAwait(false);
        }

查詢結果

{
  "id""e8976d0a-6ee9-4e2e-b8d8-1fe6e85b727b",
  "account""azrng",
  "password""123456",
  "money": 0,
  "deleted"false,
  "creater""azrng",
  "createTime""2021-05-09T15:48:45.730302",
  "modifyer""azrng",
  "modifyTime""2021-05-09T15:48:45.730425"
}

參考文檔

實體類型:https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=data-annotations

實體屬性:https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-properties?tabs=data-annotations%2Cwithout-nrt

微信公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章