c# - EF Core 嵌套的 Linq 选择结果在 N + 1 个 SQL 查询中

标签 c# sql-server linq entity-framework-core select-n-plus-1

我有一个数据模型,其中“顶级”对象具有 0 到 N 个“子”对象。在 SQL 中,这是通过外键 dbo.Sub.TopId 实现的。 .

var query = context.Top
    //.Include(t => t.Sub) Doesn't seem to do anything
    .Select(t => new {
        prop1 = t.C1,
        prop2 = t.Sub.Select(s => new {
            prop21 = s.C3 //C3 is a column in the table 'Sub'
        })
        //.ToArray() results in N + 1 queries
    });
var res = query.ToArray();

在 Entity Framework 6(延迟加载关闭)中,此 Linq 查询将转换为单个 SQL 查询。结果将被完全加载,所以 res[0].prop2将是 IEnumerable<SomeAnonymousType>已经填满了。

当使用 EntityFrameworkCore (NuGet v1.1.0) 时,子集合尚未加载且类型为:

System.Linq.Enumerable.WhereSelectEnumerableIterator<Microsoft.EntityFrameworkCore.Storage.ValueBuffer, <>f__AnonymousType1<string>>.

在您迭代数据之前不会加载数据,从而导致 N + 1 次查询。当我添加 .ToArray()对于查询(如评论中所示),数据已完全加载到 var res 中,但是使用 SQL 分析器显示这不再是在 1 个 SQL 查询中实现的。对于每个“顶级”对象,都会执行对“子”表的查询。

首先指定.Include(t => t.Sub)似乎没有任何改变。使用匿名类型似乎也不是问题,取代了 new { ... } block new MyPocoClass { ... }不会改变任何东西。

我的问题是:有没有办法获得类似于 EF6 的行为,即立即加载所有数据?


注意:我意识到在这个例子中,问题可以通过在内存中创建匿名对象来解决,执行查询之后:

var query2 = context.Top
    .Include(t => t.Sub)
    .ToArray()
    .Select(t => new //... select what is needed, fill anonymous types

但这只是一个例子,我确实需要创建对象作为 Linq 查询的一部分,因为 AutoMapper 使用它来填充我项目中的 DTO


更新:使用新的 EF Core 2.0 进行测试,问题仍然存在。 (21-08-2017)

问题已跟踪 aspnet/EntityFrameworkCore GitHub repo :Issue 4007

更新:一年后,此问题已在版本 2.1.0-preview1-final 中修复. (2018-03-01)

更新:EF 2.1 版已发布,其中包含一个修复程序。请参阅下面的答案。 (2018-05-31)

最佳答案

GitHub 问题 #4007已标记为里程碑 2.1.0-preview1closed-fixed。现在 2.1 preview1 已在 NuGet 上可用正如本 .NET Blog post 中所讨论的.

2.1 版本正式发布,使用以下命令安装:

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 2.1.0

然后在嵌套的 .Select(x => ...) 上使用 .ToList() 以指示应立即获取结果。对于我最初的问题,这看起来像这样:

var query = context.Top
    .Select(t => new {
        prop1 = t.C1,
        prop2 = t.Sub.Select(s => new {
            prop21 = s.C3
        })
        .ToList() // <-- Add this
    });
var res = query.ToArray(); // Execute the Linq query

这导致在数据库上运行 2 个 SQL 查询(而不是 N + 1);首先是普通的 SELECT FROM 'Top' 表,然后是 SELECT FROM 'Sub' 表INNER JOIN FROM 'Top' 表,基于 Key-ForeignKey 关系 [Sub].[TopId] = [Top].[Id]。然后将这些查询的结果组合在内存中。

结果正是您所期望的,并且与 EF6 返回的结果非常相似:匿名类型 'a 的数组,它具有属性 prop1 prop2 其中 prop2 是匿名类型 'b 的列表,它具有属性 prop21。最重要的是,所有这些都在 .ToArray() 调用后完全加载!

关于c# - EF Core 嵌套的 Linq 选择结果在 N + 1 个 SQL 查询中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41573491/

相关文章:

c# - 在 C# 中将 VML 渲染为位图

sql - 使用来自另一个表的唯一值创建一个表

python - 如何使用 py(py)odbc 从 python 连接到远程 MS SQL Server

.net - 尝试获取进程的窗口句柄的异常消息

c# - 用 Linq 合并 List<T>?

c# - 如果我在 MongoDB 上使用 LINQ,为什么会失去性能?

c# - Sitecore 500 错误记录/警报

c# - 使用星号的图表(循环、数组)

c# - 如何通过某种模式解析字符串并将结果标记到字典中?

sql-server - SQL Server : Cannot open database requested by the login