EF Core 2.1 中的新增功能

除了大量缺陷修复以及小规模功能增强和性能增强之外,EF Core 2.1 还新增了一些有吸引力的功能:

延迟加载

EF Core 现包含创作可按需加载导航属性的实体类所必需的构建基块。 我们还创建了一个新包 - Microsoft.EntityFrameworkCore.Proxies,它可利用这些构建基块基于最小修改实体类(例如具有虚拟导航属性的类)来生成延迟加载代理类。

阅读有关延迟加载的部分详细了解本主题。

实体构造函数中的参数

作为延迟加载所必需的一个构建基块,我们启用了实体创建,实体可将参数纳入其构造函数中。 可使用参数来注入属性值、延迟加载委托和服务。

阅读有关含参数的实体构造函数部分详细了解本主题。

值转换

到目前为止,EF Core 只能映射底层数据库提供程序本机支持的属性类型。 在列和属性之间来回复制值,无需进行任何转换。 自 EF Core 2.1 起,可通过值转换来转换从列获取的值,之后再将其应用到属性,反之亦然。 我们具有可以根据约定按需应用的大量转换,以及显式配置 API,后者允许在列和属性之间注册自定义转换。 此功能有如下一些用法:

  • 将枚举存储为字符串
  • 使用 SQL Server 映射无符号整数
  • 自动加密和解密属性值

阅读有关值转换的部分详细了解本主题。

LINQ GroupBy 转换

在 2.1 版之前,EF Core 中的 GroupBy LINQ 运算符将始终在内存中进行计算。 在大多数情况下,我们现在支持将其转换为 SQL GROUP BY 子句。

此示例显示了一个用 GroupBy 来计算各种聚合函数的查询:

var query = context.Orders
    .GroupBy(o => new { o.CustomerId, o.EmployeeId })
    .Select(g => new
        {
          g.Key.CustomerId,
          g.Key.EmployeeId,
          Sum = g.Sum(o => o.Amount),
          Min = g.Min(o => o.Amount),
          Max = g.Max(o => o.Amount),
          Avg = g.Average(o => o.Amount)
        });

相应的 SQL 转化如下所示:

SELECT [o].[CustomerId], [o].[EmployeeId],
    SUM([o].[Amount]), MIN([o].[Amount]), MAX([o].[Amount]), AVG([o].[Amount])
FROM [Orders] AS [o]
GROUP BY [o].[CustomerId], [o].[EmployeeId];

数据种子设定

新版本可提供初始数据来填充数据库。 与 EF6 不同,种子设定数据作为模型配置的一部分与实体类型相关联。 随后,将数据库升级为新版本模型时,EF Core 迁移可以自动计算需要应用的插入、更新或删除操作。

如示例所示,可使用它在 OnModelCreating 中为 Post 配置种子数据:

modelBuilder.Entity<Post>().HasData(new Post{ Id = 1, Text = "Hello World!" });

阅读有关数据种子设定的部分详细了解本主题。

查询类型

EF Core 模型现可包含查询类型。 与实体类型不同,查询类型上未定义键,也不能插入、删除或更新查询类型(即它们为只读),但查询可直接返回查询类型。 以下是查询类型的一些用法:

  • 映射到没有主键的视图
  • 映射到没有主键的表
  • 映射到模型中定义的查询
  • 用作 FromSql() 查询的返回类型

阅读有关查询类型的部分详细了解本主题。

针对派生类型的 Include

现可在编写 Include 方法的表达式时指定仅在派生类型上定义的导航属性。 对于 Include 的强类型版本,我们支持使用显式强制转换或 as 运算符。 我们现在还支持在 Include 的字符串版本中引用在派生类型上定义的导航属性的名称:

var option1 = context.People.Include(p => ((Student)p).School);
var option2 = context.People.Include(p => (p as Student).School);
var option3 = context.People.Include("School");

阅读有关派生类型的 Include 部分详细了解本主题。

System.Transactions 支持

我们增加了对 System.Transactions 功能(如 TransactionScope)的应用。 使用支持该功能的数据库提供程序时,这将适用于 .NET Framework 和 .NET Core。

阅读有关 System.Transactions 的部分详细了解本主题。

初始迁移时生成更好的列顺序

根据客户反馈,我们对迁移进行了更新,使得先以与类中声明的属性相同的顺序为表生成列。 请注意,在创建初始表后,添加新成员时,EF Core 不能更改顺序。

相关子查询优化

我们改进了查询转换,避免在许多常见情况下执行“N + 1”SQL 查询,一般情况下,在投影中使用导航属性后,来自根查询的数据会与来自相关子查询的数据相连接。 进行优化需要缓冲子查询的结果,且我们要求修改查询,选择新行为。

例如,以下查询通常会转换为:一个“客户”查询,加上 N(其中“N”是返回的客户数量)个单独的“订单”查询:

var query = context.Customers.Select(
    c => c.Orders.Where(o => o.Amount  > 100).Select(o => o.Amount));

ToList() 放入正确的位置,指示缓冲适用于订单,即可启用优化:

var query = context.Customers.Select(
    c => c.Orders.Where(o => o.Amount  > 100).Select(o => o.Amount).ToList());

请注意,此查询只会被转换为两个 SQL 查询:一个“客户”查询,一个“订单”查询。

[Owned] 属性

现只需使用 [Owned] 注释类型,并确保所有者实体添加到了模型中,即可配置固有实体类型

[Owned]
public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

.NET Core SDK 中包含的命令行工具 dotnet-ef

dotnet-ef 命令现在是 .NET Core SDK 的一部分,因此无须在项目中使用 DotNetCliToolReference 即可使用各项迁移,或通过现有数据库搭建 DbContext 基架

有关如何为不同版本的 .NET Core SDK 和 EF Core 启用命令行工具的详细信息,请参阅安装工具的相关部分。

Microsoft.EntityFrameworkCore.Abstractions 包

可以在项目中使用新包内的一些属性和接口,从而启用 EF Core 功能,而无需依赖 EF Core 整体。 例如,[Owned] 属性和 ILazyLoader 接口位于此处。

状态更改事件

ChangeTracker 中新增的 TrackedStateChanged 事件可用于编写逻辑,以响应进入 DbContext 或状态更改的实体。

原始 SQL 参数分析器

EF Core 随附新增一个代码分析器,用于检测原始 SQL API(如 FromSqlExecuteSqlCommand)的潜在不安全用法。 例如,对于下面的查询,将会看到一条警告,因为 minAge 未参数化

var sql = $"SELECT * FROM People WHERE Age > {minAge}";
var query = context.People.FromSql(sql);

数据库提供程序兼容性

建议配合使用 EF Core 2.1 以及已更新或至少已经过测试可用于 EF Core 2.1 的提供程序。

提示

如果新功能出现任何意外的不兼容或问题,或你有任何相关反馈,请使用我们的问题跟踪器进行报告。