除了许多 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
中使用新的 Tracked
和 StateChanged
事件来编写逻辑,以响应实体进入 DbContext 或更改其状态。
原始 SQL 参数分析器
EF Core 随附了一个新的代码分析器,用于检测原始 SQL API(如 FromSql
或 ExecuteSqlCommand
)的潜在不安全使用情况。 例如,对于以下查询,你将看到一个警告,因为 minAge 未进行参数化。
var sql = $"SELECT * FROM People WHERE Age > {minAge}";
var query = context.People.FromSql(sql);
数据库提供程序兼容性
建议配合使用 EF Core 2.1 以及已更新或至少已经过测试可用于 EF Core 2.1 的提供程序。
提示
如果在新功能中发现任何意外的不兼容或任何问题,或者你对其有反馈,请使用 我们的问题跟踪器进行报告。