《ASP.NET Core技术内幕与项目实战》精简集-EFCore2.2:基本使用(DbContext和迁移)
本节内容,涉及4.2(P75-P83)、7.3(P193-P197)。主要NuGet包:
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Microsoft.EntityFrameworkCore.Relational
一、在控制台程序中使用EFCore
1 //第一步:创建实体类 2 //Book.cs 3 public class Book 4 { 5 public long Id { get; set; } 6 public string? Title { get; set; } 7 public DateTime PubTime { get; set; } 8 public double Price { get; set; } 9 public string? AuthorName { get; set; } 10 } 11 12 13 //第二步:创建实体配置类,配置实体与数据表的映射关系 14 //BookConfig.cs 15 public class BookConfig : IEntityTypeConfiguration<Book> 16 { 17 public void Configure(EntityTypeBuilder<Book> builder) 18 { 19 builder.ToTable("T_Books"); 20 builder.Property(x => x.Title).HasMaxLength(50).IsRequired(); 21 builder.Property(x => x.AuthorName).HasMaxLength(20).IsRequired(); 22 } 23 } 24 25 26 //第三步:创建DbContext的子类 27 public class TestDbContext: DbContext 28 { 29 public DbSet<Book> Books { get; set; } 30 31 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 32 { 33 string connStr = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=EFCoreDemo;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"; 34 optionsBuilder.UseSqlServer(connStr); 35 } 36 37 protected override void OnModelCreating(ModelBuilder modelBuilder) 38 { 39 base.OnModelCreating(modelBuilder); 40 modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); 41 } 42 } 43 44 45 //第四步:创建DbContext对象,通过ORM操作数据库,进行增删改查。Program.cs 46 using var ctx = new TestDbContext(); 47 48 //新增 49 var b1 = new Book 50 { 51 Title = "书名1", 52 AuthorName = "作者1", 53 Price = 59.8, 54 PubTime = new DateTime(2019, 3, 1) 55 }; 56 var b2 = new Book 57 { 58 Title = "书名2", 59 AuthorName = "作者2", 60 Price = 49.8, 61 PubTime = new DateTime(2005, 3, 1) 62 }; 63 var b3 = new Book 64 { 65 Title = "书名3", 66 AuthorName = "作者3", 67 Price = 39.8, 68 PubTime = new DateTime(2010, 3, 1) 69 }; 70 ctx.Books.Add(b1); 71 ctx.Books.Add(b2); 72 ctx.Books.Add(b3); 73 await ctx.SaveChangesAsync(); 74 75 //查询 76 foreach (var b1 in ctx.Books.Where(b => b.Price > 40)) 77 { 78 Console.WriteLine($"ID = {b1.Id}, Title = {b1.Title}, author = {b1.AuthorName}, price = {b1.Price}"); 79 } 80 81 //修改 82 var b2 = ctx.Books.Single(b => b.Id == 2); 83 b2.AuthorName = "作者XX"; 84 await ctx.SaveChangesAsync(); 85 86 //删除 87 var b3 = ctx.Books.Single(b => b.Id == 3); 88 ctx.Books.Remove(b3); //也可以写成ctx.Remove(b3) 89 await ctx.SaveChangesAsync();
代码解读:
15-23行:配置实体与数据表的映射关系。
19行:设置实体映射的数据表为:T_Books
20行:设置属性Title映射的字段:最长50个字符,必填
29行:设置DbSet属性Books。ctx.Books,相当于数据表对象
31-35行:重写OnConfiguring方法,设置DbContext的数据库连接字符串
37-41行:重写OnModelCreating方法,通过反射,加载所有继承了IEntityTypeConfiguration<T>的配置类。如果不用反射,也可以在这个方法里直接写实体和数据表的映射关系。
46行:创建DbContext对象,通过ORM方式操作数据库。因为DbContext类实现了IDisposable接口,所以使用using
补充说明:
①实体与数据表的映射关系,也可以在DbContext的OnModelCreating方法中配置(见下例)。书中推荐的最佳实践是创建独立的配置类,但实际项目中,会使用多DbContext或者微服务,在DbContext中进行映射关系的配置,并不会造成代码太长难读,反而有利于维护,个人选择在DbContext中进行配置。
②在第四步,使用DbContext对象前,需要进行数据迁移。打开“工具-Nuget包管理器-程序包管理控制台”,先后执行Add-Migration init和Update-database命令,其中init为自定义每次迁移的名称
二、在AspNetCore中使用EFCore
1 //创建简单的三层应用:启动层(WebApi)、Domain层(类库)、EFCore层(类库) 2 //三层引用关系:启动层引用Domain层和EFCore层、EFCore层引用Domain层 3 4 5 //第一步:在Domain层创建Book实体 6 //Book.cs 7 public class Book 8 { 9 public long Id { get; set; } 10 public string? Title { get; set; } 11 public DateTime PubTime { get; set; } 12 public double Price { get; set; } 13 public string? AuthorName { get; set; } 14 } 15 16 17 //第二步:在EFCore层创建DbContext,配置映射关系 18 //BookStoreDbContext.cs 19 public class BookStoreDbContext: DbContext 20 { 21 public DbSet<Book> Books { get; set; } 22 23 public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options):base(options) 24 { 25 26 } 27 28 protected override void OnModelCreating(ModelBuilder builder) 29 { 30 base.OnModelCreating(builder); 31 builder.Entity<Book>(b => { 32 b.ToTable("T_Books"); 33 b.Property(b => b.Title).HasMaxLength(50).IsRequired(); 34 b.Property(b => b.AuthorName).HasMaxLength(20).IsRequired(); 35 }); 36 } 37 } 38 39 40 //第三步:启动层注册DbContext服务,并配置数据库连接字符串 41 //Program.cs 42 builder.Services.AddDbContext<BookStoreDbContext>(opt => 43 { 44 string connStr = builder.Configuration.GetConnectionString("Default"); 45 opt.UseSqlServer(connStr); 46 }); 47 48 49 //第四步:在EFCore层创建IDesignTimeDbContextFactory<T>的实现类 50 //MyDesignTimeDbContextFactory.cs 51 public class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<BookStoreDbContext> 52 { 53 public BookStoreDbContext CreateDbContext(string[] args) 54 { 55 DbContextOptionsBuilder<BookStoreDbContext> builder = new(); 56 string connStr = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=BookStore;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"; 57 builder.UseSqlServer(connStr); 58 return new BookStoreDbContext(builder.Options); 59 } 60 } 61 62 63 //第五步:在EFCore层,执行数据迁移,初始化数据库 64 Add-Migration initBookStore 65 Update-database 66 67 68 //第六步:在控制器中,以依赖注入的方式使用 69 ...... 70 private readonly BookStoreDbContext ctx; 71 public TestController(BookStoreDbContext ctx) 72 { 73 this.ctx = ctx; 74 }
代码解读:
23-26行:构造函数传入数据库连接配置。其它层在创建DbContext对象(一般使用依赖注入如上例)时,传入配置参数
28-36行:实体与数据表的映射关系,直接在DbContext的OnModelCreating方法中配置
44行:读取appsettings.json,ConnectionStrings节点的Default属性。GetConnectionString方法是builder.Configuration的内置方法,专门用于读取ConnectionStrings节点
51-60行:与控制台中使用EFCore中最大的区别,只有添加这个类后,才可以进行数据库迁移
70-74行:以依赖注入方式,创建的对象,只要实现了IDisposable接口,离开作用域后,容器会自动调用Dispose方法,所以不需要调用Dispose方法,也不需使用using
补充说明:
①由于分层原因,DbContext的服务容器和EFCore一般不再同一个层,一般框架如ABP、MASA,针对这种情况,都有相应的解决方法,基础原理也如上所示,EFCore层和启动层,两个地方都要配置数据库连接
特别说明:
1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍