EF Core 2.1 中的新功能

除了许多 bug 修复和小型功能和性能增强功能外,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 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));

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

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 的提供程序。

提示

如果在新功能中发现任何意外的不兼容或任何问题,或者你对其有反馈,请使用 我们的问题跟踪器进行报告。