查缺补漏系统学习 EF Core 6 (一)

掌握 ORM 开发方式是每一个 .NET 开发者所必备的技能,而且 .NET 平台有很多优秀的 ORM 框架。

很多人都会诟病 .NET 官方标配的 Entity Framework,感觉其笨重难用、性能低下。

但其实经过多年发展,EF Core 已经是 .NET 平台中的新一代 ORM 框架。.

虽然是上一代 EF 的跨平台版本,但在其本就无比安全稳定的基础上,做了很多的改进,摒弃了一些包袱、优化了性能,丰富了扩展等。

这个系列的目的,就是全面的讲述 EF Core,让大家对其有一个系统的了解,内容较为精炼,不会有太多废话。

基础概念

想要使用 EF Core,必须安装 Microsoft.EntityFrameworkCore 库,它提供了 EF Core 核心功能。

除此之外,我们还需要安装与你使用的数据库相匹配的数据库提供程序。

比如,本文示例使用的是 SQL Server LocalDB,所以我需要安装 Microsoft.EntityFrameworkCore.SqlServer库。

模型

EF Core 是一个 ORM 框架。

什么是 ORM?也就是 「Object Relational Mapping」,对象关系映射。

这里的 Object ,在 EF Core 中,以实体类来表示。

实体类是 EF Core 用于映射到数据库表的一个类。

在这个示例中,有一个 Entities 文件夹,用来存放与 EF Core 相关的实体类,里面有一个名为 Account 的实体类。

查缺补漏系统学习 EF Core 6 (一)

EF Core 中有一系列的规则,来帮助它将实体类映射到数据库表中。

以 Account 实体类为例,这个映射过程很简单:

查缺补漏系统学习 EF Core 6 (一)

首先,EF Core 通过实体类,拥有了用于列和配置映射的表的信息。

接下来,这个类中的所有公共属性,都会被映射到表的列中,并具有与属性相同的名称。

最后,EF Core 使用命名约定,在数据库表中从 AccountId 属性中,创建一个主键。

连接字符串

EF Core 是一个面向数据库的 ORM 框架,我们就必须为它提供数据库的信息,也就是数据库连接字符串。

一般情况下,我们使用 「appsettings.json」 配置文件,为 EF Core 提供连接字符串。

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CodeMan;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

在这个示例中,提供的连接字符串是,SQL Server LocalDB 中的默认实例,指定的数据库名称是 「CodeMan」

需要注意的是,由于连接字符串属于敏感信息,所以使用配置文件来存储连接字符串,并不是最好的做法,特别是对于生产环境。

那么针对生产环境,更好的方法就是使用环境变量来提供诸如连接字符串的敏感信息。

依赖注入

EF Core 中有一个非常重要的东西,就是数据上下文类。

在这个示例中,Entities 目录中的 ApplicationContext 类型,就是 EF Core 在当前应用中的上下文类。

查缺补漏系统学习 EF Core 6 (一)

这个类必须继承自 DbContext 基类,它包含访问数据库的信息和配置。

构造函数使用 DbContextOptions 参数,为基类提供额外的选项。

其中最重要的是 DbSet<Account> 类型的 Accounts 属性。

EF Core 会在上下文类中,查找所有的公共 DbSet 属性,将它们的名称映射到数据库中的表的名称。

然后,它会进入 DbSet<T> 提供的泛型类,在我们的例子中,它是一个 Account 类,并将所有的公共属性,映射到表中具有相同名称和类型的列。

如果 Account 类对其他类有任何引用,EF Core 还会使用这些引用属性,并在数据库中创建相应的关系。

让我们回到在 Main 方法中:

查缺补漏系统学习 EF Core 6 (一)

首先,我们创建了一个读取 appsettings.json 配置文件的配置对象,用来获取连接字符串

然后,在依赖注入系统中,通过使用 AddDbContext 方法,来注册 ApplicationContext 上下文类

在它的配置委托中,使用 UseSqlServer 方法,设置连接字符串。

在这个示例中,还注册了一个 AccountService 服务,该服务的实现类,通过构造函数注入了上下文实例。

这样我们就可以在服务类中,使用上下文实例。

上下文池

需要注意的是,在EF Core 中,注册上下文类的方法有两种:

AddDbContext 方法注册的上下文类型,生命周期模式默认是作用域,这个作用域的范围是线程,在 ASP.NET Core 应用中作用域的范围表现为一个请求。

因为上下文实例是非线程安全的,所以我们一般不需要去修改它的生命周期模式。

另外还有一个 AddDbContextPool 方法,它用来配置上下文实例池。

查缺补漏系统学习 EF Core 6 (一)

上下文池可以重复使用上下文实例,而不是为每个请求创建新的实例。

当应用请求上下文实例时 EF Core 会首先检查池中,是否有可用的实例。

请求处理完成后,上下文实例的任何状态都会被重置,然后回到池中。

也就是说,保存在池中的可以复用的上下文实例,它不会再被初始化。所以,它不太适合在每一次使用它,都需要初始化的场景。

上下文池可以提高大型 Web 应用的吞吐量。默认情况下,池中保持的上下文实例数是 1024 个,这个值可以通过 poolSize 参数进行修改,示例中修改为 2048 个。

一旦池中保存的实例数超出池大小,就不会再保存新的上下文实例,此时 EF Core 会回退到「按需创建实例的非池行为」

池的大小需要根据实际情况去设置,池容量太小,如果不够用就会回退到非池行为,此时池的意义不大。

如果池容量太大,则可能消耗不必要的内存,因为未使用的上下文实例,也保留在池中。

一般情况下,池的性能提升通常可以忽略不计,除非是在高度优化的方案中。

上下文类

我们现在再回头看一看 ApplicationContext 上下文类:

查缺补漏系统学习 EF Core 6 (一)

它的构造函数中,接受了一个 DbContextOptions 类型的参数,这个参数用来提供上下文的选项。

除了直接使用 DbContextOptions 类型,还可以使用泛型版本 DbContextOptions 类型。

public ApplicationContext(DbContextOptions<ApplicationContext> options)

无论使用哪个版本,我们的应用都会正常工作。

使用哪一个版本,取决于你的应用中是否有多种上下文类型。

如果应用中有多种上下文类型,就推荐使用泛型版本,而当前示例的情况并非如此,所以使用的是非泛型版本。

DbContext有三个重要的属性:

  • Database 属性负责与数据库的交互、数据库的迁移/创建和原始 SQL 查询。

  • ChangeTracker 属性用于跟踪通过同一上下文实例,查询出的实体状态。

  • Model 属性提供了对 EF Core 在连接,或创建数据库时,使用的数据库模型。

我们可以使用 Model 属性,来访问每个实体及其属性的信息。

不过,这需要我们安装 Microsoft.EntityFrameworkCore.Relationa 库。

然后在 AccountService 实现类的 Run 方法中,就可以通过使用上下文实例的 Model 属性,找到上下文中的任意实体,并获取它的一些信息,比如表名和主键字段名。

小结

这篇文章,讲述了 EF Core 的一些基本概念:模型、连接字符串、依赖注册、上下文池与上下文类。

下篇文章,我们将盘点一下 EF Core 的属性配置。