EF Core 10 中的新增功能

EF Core 10(EF10)是 EF Core 9 之后的下一个版本,计划在 2025 年 11 月发布。

EF10 以预览版的形式提供。 请参阅 .NET 10 发行说明 获取有关最新预览版的信息。 本文将在提供新的预览版时更新。

提示

可通过从 GitHub 下载示例代码来运行和调试示例。 下面的每个部分都链接到特定于该部分的源代码。

EF10 需要 .NET 10 SDK 才能生成,并且需要运行 .NET 10 运行时。 EF10 不会在早期 .NET 版本上运行,也不会在 .NET Framework 上运行。

用于 NoSQL 的 Azure Cosmos DB

全文搜索支持

Azure Cosmos DB 现在支持 全文搜索。 它可实现高效有效的文本搜索,以及评估文档与给定搜索查询的相关性。 它可以与矢量搜索结合使用,以提高某些 AI 方案中的响应的准确性。 EF Core 10 正在添加对此功能的支持,允许在面向 Azure Cosmos DB 的查询中使用启用了全文搜索的属性对数据库建模,并使用全文搜索函数。

下面是一种基本 EF 模型配置,用于对其中一个属性启用全文搜索:

public class Blog
{
    ...

    public string Contents { get; set; }
}

public class BloggingContext
{
    ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>(b =>
        {
            b.Property(x => x.Contents).EnableFullTextSearch();
            b.HasIndex(x => x.Contents).IsFullTextIndex();
        });
    }
}

配置模型后,我们可以利用EF.Functions中提供的方法在查询中执行全文搜索操作。

var cosmosBlogs = await context.Blogs.Where(x => EF.Functions.FullTextContains(x.Contents, "cosmos")).ToListAsync();

目前支持以下全文操作:FullTextContainsFullTextContainsAllFullTextContainsAnyFullTextScore

有关 Cosmos 全文搜索的详细信息,请参阅 文档

EF Core 现在支持 RRF (倒数排名融合) 函数,该函数结合了矢量相似性搜索和全文搜索(即混合搜索)。 下面是使用混合搜索的示例查询:

float[] myVector = /* generate vector data from text, image, etc. */
var hybrid = await context.Blogs.OrderBy(x => EF.Functions.Rrf(
        EF.Functions.FullTextScore(x.Contents, "database"), 
        EF.Functions.VectorDistance(x.Vector, myVector)))
    .Take(10)
    .ToListAsync();

有关 Cosmos 混合搜索的详细信息,请参阅 文档

矢量相似性搜索退出预览

在 EF9 中,添加了 对矢量相似性搜索的实验性支持。 在 EF Core 10 中,矢量相似性搜索支持不再具有实验性。 我们还对该功能进行了一些改进:

  • EF Core 现在可以使用在拥有的引用实体上定义的矢量属性生成容器。 使用在拥有的集合上定义的矢量属性的容器仍需通过其他方式创建。 但是,可以在查询中使用它们。
  • 模型生成 API 已重命名。 现在可以使用该方法配置 IsVectorProperty 向量属性,并且可以使用该方法 IsVectorIndex 配置向量索引。

有关 Cosmos 矢量搜索的详细信息,请参阅 文档

提升模型进化时的体验

在以前版本的 EF Core 中,使用 Azure Cosmos DB 时,改进模型相当痛苦。 具体而言,在向实体添加新的必需属性时,EF 将无法再具体化该实体。 原因是 EF 预期新属性的值(因为它是必需的),但在更改之前创建的文档不包含这些值。 解决方法是先将属性标记为可选,手动添加属性的默认值,然后才将其更改为必需。

在 EF 10 中,我们改进了此体验 - 如果文档中缺少相关数据,EF 现在将为必需属性生成默认值,而不是抛出异常。

LINQ 和 SQL 翻译

支持 .NET 10 LeftJoinRightJoin 运算符

使用 EF Core 时,LEFT JOIN 是一种常见且有用的操作。 在以前的版本中,在 LINQ 中实现 LEFT JOIN 相当复杂,需要在特定配置 中执行SelectManyGroupJoinDefaultIfEmpty 操作

.NET 10 添加了对 LeftJoin 方法的一流 LINQ 支持,使这些查询更易于编写。 EF Core 可识别新方法,因此可以在 EF LINQ 查询中使用,而不是旧构造:

var query = context.Students
    .LeftJoin(
        context.Departments,
        student => student.DepartmentID,
        department => department.ID,
        (student, department) => new 
        { 
            student.FirstName,
            student.LastName,
            Department = department.Name ?? "[NONE]"
        });

注释

EF 10 还支持类似 RightJoin 运算符,该运算符将所有数据从第二个集合中保留,并且只保留第一个集合中的匹配数据。 EF 10 将此转换为数据库中的 RIGHT JOIN 操作。

有关详细信息,请参阅 #12793#35367

其他查询改进

  • 翻译 DateOnly.ToDateTime(timeOnly) (#35194,由 @mseada94 贡献)。
  • 优化多个连续的 LIMIT#35384,由 @ranma42 贡献)。
  • 优化在Count上的ICollection<T>操作(#35381,由@ChrisJollyAU贡献)。
  • 优化 MIN/MAX 方面的 DISTINCT#34699,由 @ranma42贡献)。
  • 使用DatePart.MicrosecondDatePart.Nanosecond参数转换日期/时间函数(#34861)。
  • 简化参数名称(例如 from @__city_0city) (#35200)。
  • 在 SQL Server 上,将COALESCE翻译为ISNULL,在大多数情况下(#34171,由@ranma42贡献)。
  • 支持一些以char为参数的字符串函数(#34999,由@ChrisJollyAU贡献)。
  • 支持 MAX/MIN/ORDER BY 在 SQLite 上使用 decimal#35606,由 @ranma42 贡献)。
  • 支持通过条件运算符投射不同的导航(但类型相同)(#34589,由 @ranma42 贡献)。

ExecuteUpdateAsync 现接受常规的非表达式 lambda

ExecuteUpdateAsync 可用于表示数据库中的任意更新操作。 在以前的版本中,通过表达式树参数提供对数据库行执行的更改;这使得动态生成这些更改非常困难。 例如,假设我们要更新博客的浏览量,但根据条件还需要更新其名称。 由于 setters 参数是表达式树,因此需要编写以下代码:

// Base setters - update the Views only
Expression<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>> setters =
    s => s.SetProperty(b => b.Views, 8);

// Conditionally add SetProperty(b => b.Name, "foo") to setters, based on the value of nameChanged
if (nameChanged)
{
    var blogParameter = Expression.Parameter(typeof(Blog), "b");

    setters = Expression.Lambda<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>>(
        Expression.Call(
            instance: setters.Body,
            methodName: nameof(SetPropertyCalls<Blog>.SetProperty),
            typeArguments: [typeof(string)],
            arguments:
            [
                Expression.Lambda<Func<Blog, string>>(Expression.Property(blogParameter, nameof(Blog.Name)), blogParameter),
                Expression.Constant("foo")
            ]),
        setters.Parameters);
}

await context.Blogs.ExecuteUpdateAsync(setters);

手动创建表达式树非常复杂且容易出错,并且使此常见方案比应该的要困难得多。 从 EF 10 开始,现在可以改为编写以下代码:

await context.Blogs.ExecuteUpdateAsync(s =>
{
    s.SetProperty(b => b.Views, 8);
    if (nameChanged)
    {
        s.SetProperty(b => b.Name, "foo");
    }
});

感谢 @aradalvand 提出并推动此次更改(在 #32018)。

自定义默认约束名称

在以前版本的 EF Core 中,为属性指定默认值时,EF Core 始终允许数据库自动生成约束名称。 现在,可以显式指定 SQL Server 的默认值约束的名称,从而更好地控制数据库架构。

现在可以在模型配置中定义默认值时指定约束名称:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.IsActive)
        .HasDefaultValue(true, "DF_Blog_IsActive");

    modelBuilder.Entity<Post>()
        .Property(p => b.CreatedDate)
        .HasDefaultValueSql("GETDATE()", "DF_Post_CreatedDate");
}

还可以调用 UseNamedDefaultConstraints 以启用所有默认约束的自动命名。 请注意,如果有现有迁移,则添加的下一个迁移将重命名模型中的每一个默认约束。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.UseNamedDefaultConstraints();
}

其他改进

  • 使 SQL Server 基架与 Azure 数据资源管理器兼容(#34832,由 @barnuri贡献)。
  • 将 DatabaseRoot 与作用域选项实例相关联,而不是单例选项(#34477,由 @koenigst贡献)。
  • 关闭敏感日志记录时,从日志中编辑内联常量(#35724)。
  • 改进 LoadExtension,使其能够在 dotnet run 和以 lib* 命名的库(#35617,由 @krwq 提供)中正常工作。
  • 将 AsyncLocal 更改为 ThreadId 以提高延迟加载程序性能(#35835,由 @henriquewr贡献)。