jk's notes
  • 数据库上下文

数据库上下文

本章中, 我会介绍什么是 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 提供了很多配置来将你的实体模型映射到目标数据库中. 下一章中, 我会创建并允许初始化数据库迁移, 它会通过代码创建数据库.

Last Updated:
Contributors: jk