.NET之生成數(shù)據(jù)庫(kù)全流程
本文轉(zhuǎn)載自微信公眾號(hào)「鵬祥」,作者AZRNG。轉(zhuǎn)載本文請(qǐng)聯(lián)系鵬祥公眾號(hào)。
開篇語(yǔ)
本文主要是回顧下從項(xiàng)目創(chuàng)建到生成數(shù)據(jù)到數(shù)據(jù)庫(kù)(代碼優(yōu)先)的全部過程。采用EFCore作為ORM框架。
本次示例環(huán)境:vs2019、net5、mysql
創(chuàng)建項(xiàng)目
本次事例代碼是用過vs2019創(chuàng)建的ASP.NET Core Web API項(xiàng)目
可以通過可視化界面創(chuàng)建或者通過命令行創(chuàng)建
- dotnet new webapi -o Net5ByDocker
創(chuàng)建實(shí)體類
安裝連接MySQL數(shù)據(jù)庫(kù)組件
- <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0" />
- <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Json.Newtonsoft" Version="5.0.0" />
增加實(shí)體類
- [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("帳號(hào)")]
- [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("創(chuàng)建人")]
- [StringLength(20)]
- [Required]
- public string Creater { get; private set; }
- [Comment("創(chuàng)建時(shí)間")]
- [Required]
- public DateTime CreateTime { get; private set; }
- [Comment("修改人")]
- [StringLength(20)]
- [Required]
- public string Modifyer { get; private set; }
- [Comment("修改時(shí)間")]
- [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;
- }
- }
這種只是增加實(shí)體類類型的一種方式,可能這種看著比較亂,還可以通過OnModelCreating實(shí)現(xiàn),詳情看參考文檔
增加數(shù)據(jù)庫(kù)上下文OpenDbContext
- public class OpenDbContext : DbContext
- {
- public OpenDbContext(DbContextOptions<OpenDbContext> options)
- : base(options)
- {
- }
- public DbSet<User> Users { get; set; }
- }
Startup注入連接數(shù)據(jù)庫(kù)操作
- 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
結(jié)果
image.png
要看下生成的遷移文件是否是自己預(yù)期的那樣子,也可以在這一步就生成數(shù)據(jù)庫(kù),命令:Update-Database
數(shù)據(jù)種子
增加OpenDbSend類,添加數(shù)據(jù)種子
- public class OpenDbSend
- {
- /// <summary>
- /// 生成數(shù)據(jù)庫(kù)以及數(shù)據(jù)種子
- /// </summary>
- /// <param name="dbContext">數(shù)據(jù)庫(kù)上下文</param>
- /// <param name="loggerFactory">日志</param>
- /// <param name="retry">重試次數(shù)</param>
- /// <returns></returns>
- public static async Task SeedAsync(OpenDbContext dbContext,
- ILoggerFactory loggerFactory,
- int? retry = 0)
- {
- int retryForAvailability = retry.Value;
- try
- {
- dbContext.Database.Migrate();//如果當(dāng)前數(shù)據(jù)庫(kù)不存在按照當(dāng)前 model 創(chuàng)建,如果存在則將數(shù)據(jù)庫(kù)調(diào)整到和當(dāng)前 model 匹配
- await InitializeAsync(dbContext).ConfigureAwait(false);
- //if (dbContext.Database.EnsureCreated())//如果當(dāng)前數(shù)據(jù)庫(kù)不存在按照當(dāng)前 model創(chuàng)建,如果存在則不管了。
- // 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>
- /// 初始化數(shù)據(jù)
- /// </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);
- }
- }
設(shè)置項(xiàng)目啟動(dòng)時(shí)候調(diào)用
- 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, $"項(xiàng)目啟動(dòng)出錯(cuò) {ex.Message}");
- }
- }
- await host.RunAsync().ConfigureAwait(false);
- }
生成數(shù)據(jù)庫(kù)
啟動(dòng)項(xiàng)目,自動(dòng)生成數(shù)據(jù)庫(kù)
image.png
表結(jié)構(gòu)如下
image.png
如果后期數(shù)據(jù)庫(kù)字段或者結(jié)構(gòu)有變動(dòng),可以再次生成遷移文件然后生成數(shù)據(jù)庫(kù)
查詢數(shù)據(jù)
- /// <summary>
- /// 用戶接口
- /// </summary>
- public interface IUserService
- {
- string GetName();
- /// <summary>
- /// 查詢用戶信息
- /// </summary>
- /// <param name="account"></param>
- /// <returns></returns>
- Task<User> GetDetailsAsync(string account);
- }
- /// <summary>
- /// 用戶實(shí)現(xiàn)
- /// </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類,然后只查詢需要的內(nèi)容,不直接返回實(shí)體類
控制器方法
- /// <summary>
- /// 查詢用戶詳情
- /// </summary>
- /// <param name="account"></param>
- /// <returns></returns>
- [HttpGet]
- public async Task<ActionResult<User>> GetDetailsAsync(string account)
- {
- return await _userService.GetDetailsAsync(account).ConfigureAwait(false);
- }
查詢結(jié)果
- {
- "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"
- }
參考文檔
實(shí)體類型:https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=data-annotations
實(shí)體屬性:https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-properties?tabs=data-annotations%2Cwithout-nrt