我有一个数据模型,其中“顶级”对象具有 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-preview1
的 closed-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/