数据库上下文
本章中, 我会介绍什么是 Entity Framework Core 5 的数据库上下文, 以及如何进行配置. 通过继承于 DbContext
的数据库上下文, 可以连接到数据库存储上. 通过 DbContext
, 你可以执行创建, 读取, 更新, 以及删除操作. 除此之外, 你还可以执行聚合, 诸如 sum
, max
, average
, 和 count
.
创建一个简单的数据库上下文
开始, 创建一个名为 AppDbContext
的类, 它继承于 DbContext
, 如下所示:
using Microsoft.EntityFrameworkCore;
namespace EFCore5WebApp.DAL
{
public class AppDbContext: DbContext
{
}
}
连接到我们的数据库
Entity Framework Core 5 有很多数据库 provider. 并且你要使用 provider 可以由 DbContextOptions
的 fluent API 来构建. 要实现这个功能, 我们可以定义构造器. 如下所示:
using Microsoft.EntityFrameworkCore;
namespace EFCore5WebApp.DAL
{
public class AppDbContext: DbContext
{
public AppDbContext() : base()
{
}
public AppDbContext(DbContextOptions options) : base(options)
{
}
}
}
例如, 要连接到名为 DemoDb 的 SQL Express LocalDB 实例, 我们可以按照下面的方式创建 AppDbContext
:
var context = new AppDbContext(
new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(
"Server=(localdb)\\mssqllocaldb;Database=DemoDb;" +
"Trusted_Connection=True;MultipleActiveResultSets=true"
)
.Options
);
你可以使用其他数据库的 provider, 例如 SQLite, 可以通过 NuGet 来为 Entity Framework Core 5 安装 SQLite 数据库的 provider. 要使用 SQLite provider, 你只需要传入数据库文件名, 它会用于 DbContextOptionsBuilder
类. 例如:
var context = new AppDbContext(
new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite("Filename=Demo.db")
.Options
);
在本书后面, 我们会安装并使用 SQLite provider 来进行单元测试.
在数据库上下文中访问实体
如果你没有任何实体可访问, 数据库上下文就没有什么用. DbSet
类用于访问存储与你所选数据库中的实体. 要访问 Person
, Address
, 以及 LookUp
实体, 我们会使用 DbSet
类型的属性将其添加到数据库上下文中. 如下:
using EFCore5WebApp.Core.Entities;
using Microsoft.EntityFrameworkCore;
namespace EFCore5WebApp.DAL
{
public class AppDbContext: DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<LookUp> LookUps { get; set; }
public AppDbContext() : base()
{
}
public AppDbContext(DbContextOptions options) : base(options)
{
}
}
}
使用 DbSet
类可以访问创建, 读取, 更新, 和删除实体记录. 我们会在 7 到 10 章中介绍该细节.
保存实体修改
Entity Framework Core 5 使用 SaveChanges
方法来将数据持久化到数据库中. 在使用数据库上下文的 SaveChanges()
或 SaveChangesAsync()
方法之前不会存储任何数据到数据库中. 在这个示例中, AppDbContext
导出了从基类 DbContext
类继承来的 SaveChanges()
和 SaveChangeAsync()
方法. 也就是说, 你能添加, 更新, 移除数据, 然后通过调用 SaveChanges()
和 SaveChangesAsync()
来提交所有的修改. SaveChangesAsync()
方法是 SaveChanges()
方法的异步版本, 你可以按照下面的方式来调用:
private async Task SaveDemo() {
var context = new AppDbContext();
await context.SaveChangesAsync();
}
在创建 AppDbContext
后, 你可以执行数据库操作. 然后在调用 SaveChangesAsync
.
配置数据库上下文
在第三章中, 我们介绍了如何使用数据注解来配置你的实体, 来映射到会生成的数据库架构上. 替代所用的特性, 你可以使用 OnModelCreating()
方法来实现所有相同的映射. OnModelCreating()
方法会在 DbContext
创建时调用, 并用来配置 DbContext
. 有些映射必须使用 OnModelCreating()
方法来配置, 例如组合键.
大多数配置既可以使用特性实现, 也可以使用 OnModelCreating()
方法来实现. 下面让我们来做一些配置.
为所有实体设置数据库架构
你可以为所有实体设置默认映射的架构, 只需要在 OnModelCreating()
方法中的 model builder 上使用 HasDefaultSchema
fluent API 方法. 例如, 要为所有的实体设置架构为 "Custom", 你可以这么用:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Custom");
}
组合键约束
一个联合主键是一个主键约束, 它用于指示一个实体记录在多个实体属性上是唯一的. 例如, 如果我们将 Person
的 FirstName
和 LastName
组合作为 person 的主键. 我们可以在 OnModelCreating()
方法中按照下面方式定义组合键:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasKey(c => new { c.FirstName, c.LastName });
}
主键约束
如果相比于数据特性, 你更希望使用 fluent 语法, 你也可以指定单列主键, 通过使用 HasKey
方法. 例如, 定义 LookUp
实体的 "Code" 列为主键, 我们可以使用这个代码:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LookUp>().HasKey(c => c.Code);
}
更进一步, 我们可以使用 fluent API 来指定目标数据库中主键约束的名字. 默认的约束名是 PK_EntityName
. 例如, 修改 lookup
的主键约束名为 "PK_LookUp_Code"
, 你可以这么写:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LookUp>().HasKey(c => c.Code)
.HasName("PK_LookUp_Code");
}
默认属性值
你可以在 OnModelCreating
方法中使用模型 builder API 来设置默认属性值. 该值既可以是硬编码值, 也可以是数据库中的值, 例如 SQL Server 方法 getdate()
.
硬编码值
例如, 要在一个 address
记录中设置默认国家为 "USA", 你可以使用下面代码来进行设置:
modelBuilder.Entity<Address>()
.Property(b => b.Country)
.HasDefaultValue("USA");
SQL 计算默认值
你可以使用模型 builder API HasDefaultValueSql
方法来设置默认值, 该值由数据库中计算得到. 例如, 将 Person
实体的 CreatedOn
值设置为 SQL Server 方法 "getdate()"
的默认值, 你的代码可以写成:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Property(p => p.CreatedOn)
.HasDefaultValueSql("getdate()");
}
外键关系
你也可以使用 模型 builder API 来完全控制配置, 以及如何定义外键关系. 我们会在第11章介绍大多数通用案例.
小结
本章中, 我们介绍了大量有关创建与配置数据库上下文的内容. 我们介绍了如何创建一个简单的数据库上下文类. 然后我们介绍如何设置数据库的连接属性, 来连接到目标数据库中. 然后, 我们介绍了如何将实体添加到数据库上下文中. 然后, 我们介绍使用数据库上下文如何持久化修改. 我们还说明了在 OnModelCreating()
方法中用模型 builder API 来配置数据库上下文.
如你所看到的, Entity Framework Core 5 提供了很多配置来将你的实体模型映射到目标数据库中. 下一章中, 我会创建并允许初始化数据库迁移, 它会通过代码创建数据库.