EF Core 11 优化拆分查询:运行时间缩短、内存分配减少!
EF Core 11 让拆分查询更快运行时间缩短、内存分配减少2026 年 6 月 8 日这是一件值得开发者关注的技术优化事件。如果你在代码库中使用了 AsSplitQueryEF Core 11 会给你带来查询速度提升的惊喜。目录问题是什么有什么变化基准测试资源默认情况下EF Core 会在一个查询中加载所有内容。若使用 Include 加载一个集合会使用 JOIN 操作对集合使用 JOIN 会为每个子项复制父行连接内容越多情况越糟。AsSplitQuery 可解决该问题EF 会为根实体执行一个查询为每个额外的集合执行一个查询。不过这并非万能之策还可能导致更多往返操作且因不是原子操作涉及多个事务根实体和子项之间的状态可能发生变化。更多信息请见 。问题是什么以下是 原始问题 的设置一个引用导航属性一个集合导航属性。var result context.Blogs .Include(x x.BlogType) // 引用导航属性 .Include(x x.Posts) // 集合导航属性 .AsSplitQuery() .ToList();在 EF Core 10 及之前的版本中这会生成两个查询。SELECT [b].[Id], [b].[BlogDetailId], [b].[BlogTypeId], [b].[Name], [b0].[Id], [b0].[Type] FROM [Blogs] AS [b] LEFT JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] [b0].[Id] ORDER BY [b].[Id], [b0].[Id] SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id], [b0].[Id] FROM [Blogs] AS [b] LEFT JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] [b0].[Id] INNER JOIN [Post] AS [p] ON [b].[Id] [p].[BlogId] ORDER BY [b].[Id], [b0].[Id]看看第二个查询即获取文章的查询。其中出现 BlogType 是不必要的它连接了该表选择了其 Id 并按其排序。要将 Post 与其 Blog 匹配博客的主键就足够了。而且引用导航属性越多情况越糟若有五个引用导航属性使用 Include每个集合查询都会附带五个 JOIN 操作和五个额外的 ORDER BY 列数据库执行这些操作却丢弃结果查询计划也会受影响。有什么变化从预览版 3 开始EF Core 11 会从集合查询中移除引用导航属性。现在第二个查询就像手动编写的一样。SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id] FROM [Blogs] AS [b] INNER JOIN [Post] AS [p] ON [b].[Id] [p].[BlogId] ORDER BY [b].[Id]基准测试会在博客文章末尾提供完整设置的链接。以下是使用的一个小设置可在上面链接的 Github 问题中找到几乎相同的内容。免责声明对比了 .NET 10 中的 EF 10 和 .NET 11 中的 EF 11。需说明的是并非所有性能提升都一定仅来自 EF 11因为在基准测试中改变了两个变量。理论上也可能完全是从 .NET 10 升级到 .NET 11 的原因但这种可能性不大。[MemoryDiagnoser] [SimpleJob(RuntimeMoniker.Net10_0)] [SimpleJob(RuntimeMoniker.Net11_0)] public class SplitQueryBenchmarks { private SqliteConnection _connection null!; private DbContextOptionsBloggingContext _options null!; [GlobalSetup] public void Setup() { _connection new SqliteConnection(Data Source:memory:); _connection.Open(); _options new DbContextOptionsBuilderBloggingContext() .UseSqlite(_connection) .Options; using var context new BloggingContext(_options); context.Database.EnsureCreated(); Seeder.Seed(context, blogCount: 5_000, postsPerBlog: 5); } [GlobalCleanup] public void Cleanup() _connection.Dispose(); [Benchmark] public ListBlog SplitQuery() { using var context new BloggingContext(_options); return BlogQuery(context).AsSplitQuery().ToList(); } private static IQueryableBlog BlogQuery(BloggingContext context) context.Blogs .AsNoTracking() .Include(b b.Author) .Include(b b.Category) .Include(b b.Series) .Include(b b.Posts); }结果如下方法运行时平均值误差标准差中位数Gen0Gen1Gen2分配内存SplitQuery.NET 10.068.53 ms1.358 ms2.742 ms67.54 ms4375.00001000.0000375.000033.35 MBSplitQuery.NET 11.062.07 ms1.213 ms2.788 ms61.10 ms4111.11111333.3333444.444430.15 MB可以看到运行时间更短内存分配也更少资源本文的源代码点击查看所有的示例代码都托管在这个仓库中点击查看Github 问题https://github.com/dotnet/efcore/issues/29182你可能感兴趣的相关文章EF Core 6 - 新特性EF Core 6 即将推出看起来非常有前景。在这篇博客文章中将介绍即将推出的新特性。2021 年 7 月 12 日 阅读时长3 分钟 EF CoreC#.NET 6EF Core 6我的 EF LINQ to SQL 查询安全吗是否曾自问EF LINQ 查询是否容易受到 SQL 注入攻击是因为从文本字段中查询用户数据还是直接将 API 接收到的数据用于数据库查询2024 年 6 月 17 日 阅读时长3 分钟 C#.NETEntity FrameworkEF.NET 11 中的 Zstandard 压缩.NET 已经有 DeflateStream、GZipStream、ZLibStream 和 BrotliStream 有一段时间了。在 .NET 11 中一个新成员加入了ZstandardStream。现在可以在 .NET 中使用 “Zstd” 了。2026 年 5 月 18 日 阅读时长2 分钟 .NET.NET 11