我有一个数据模型,其中“Top”对象具有 0 到 N 个“Sub”对象。在 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 查询中实现。对于每个“Top”对象,都会执行对“Sub”表的查询。
首先指定.Include(t => t.Sub)
似乎没有改变任何东西。使用匿名类型似乎也不是问题,用替换new { ... }
块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 存储库上跟踪问题:问题 4007
更新:一年后,此问题已在版本中修复2.1.0-preview1-final
。(2018-03-01)
更新: EF 版本 2.1 已发布,其中包含一个修复程序。请参阅下面的答案。(2018-05-31)