多层项目中EFCore的使用

实体类:

1
2
3
4
5
6
7
public class Book {
public long Id { get ; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public double Price { get; set; }
public DataSetDateTime PubDate { get; set; }
}

配置类:

1
2
3
4
5
internal class BookConfig : IEntityTypeConfiguration<Book> {
public void Configure(EntityTypeBuilder<Book> builder) {
builder.ToTable("T_Books");
}
}

设计思路

1、建立类库项目,放实体类、DbContext、配置类等。
DbContext中不配置数据库连接,而是为DbContext增加一个DbContextOptions类型的构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
public  class MyDbContext:DbContext {
public DbSet<Book> Books { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options):base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly
(this.GetType().Assembly);
}
}

2、EFCore项目安装对应数据库的EFCore Provider。
3、asp.net core项目引用EFCore项目,并且通过AddDbContext来注入DbContext及对DbContext进行配置。

1
2
3
4
5
builder.Services.AddDbContext<MyDbContext>(opt => {
string connStr = builder.Configuration.GetSection("ConnStr").Value;
var serverVersion = new MySqlServerVersion(new Version(8, 0, 29));
opt.UseMySql(connStr, serverVersion);
});

4、Controller中就可以注入DbContext类使用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase {
private readonly MyDbContext dbCtx;
public TestController(MyDbContext dbCtx) {
this.dbCtx = dbCtx;
}
[HttpGet]
public string Deme1() {
int c = dbCtx.Books.Count();
return $"C={c};";
}
}

5、让开发环境的Add-Migration知道连接哪个数据库,在EFCore项目中创建一个实现了IDesignTimeDbContextFactory的类,并且在CreateDbContext返回一个连接开发数据库的DbContext。

1
2
3
4
5
6
7
8
9
10
internal class MyDbContextDesignFac : IDesignTimeDbContextFactory<MyDbContext> {
public MyDbContext CreateDbContext(string[] args) {
DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>();
string connStr = "server=localhost;user=root;password=yourpassword;database=csharptest";
var serverVersion = new MySqlServerVersion(new Version(8, 0, 29));
builder.UseMySql(connStr, serverVersion);
MyDbContext ctx = new MyDbContext(builder.Options);
return ctx;
}
}

如果不在乎连接字符串被上传到Git,可以把连接字符串写死到CreatDbContext;如果在乎。那么CreateDbContext里面很难读取到vs中通过简单的方法设置的环境变量,所以必须把连接字符串配置到windows的正式的环境变量中,然后再Environment.GetEnvironmentVariable读取。
6、正常执行Add-Migration、Update-Database迁移就行了。需要把EFCore项目设置为启动项目,并且在【程序包管理器控制台】中也要选中EFCore项目,并且安装Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools

AddDbContextPool

AddDbContext是Scope模式的依赖注入,当一个请求结束后就会销毁DbContext实例,在请求多的情况下会有一定的性能影响。
AddDbContextPool是池化技术,不用每次创建和销毁,会有一点性能提升

1
2
3
4
5
builder.Services.AddDbContextPool<MyDbContext>(opt => {
string connStr = builder.Configuration.GetSection("ConnStr").Value;
var serverVersion = new MySqlServerVersion(new Version(8, 0, 29));
opt.UseMySql(connStr, serverVersion);
});

可能带来的问题:

1、AddDbContextPool注册的DbContext无法注入其他服务,因为这样注册的DbContext是Singleton的,在长生命周期的组件中无法注入短生命周期的组件,而大多数服务都不是Singleton的,所以可以理解成无法注入其他服务。
2、很多ADO.NET提供者都实现了数据库连接池机制,可能会有冲突,使用的时候需要自己调节。