zl程序教程

您现在的位置是:首页 >  .Net

当前栏目

《ASP.NET Core技术内幕与项目实战》精简集-EFCore2.2:基本使用(DbContext和迁移)

2023-03-20 15:33:40 时间

本节内容,涉及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倍