EF Core,投影子集合急切加载

Posted

技术标签:

【中文标题】EF Core,投影子集合急切加载【英文标题】:EF Core, projection sub collection eager loading 【发布时间】:2018-11-06 15:02:12 【问题描述】:

在 EF 核心中,我试图用子集合投影出一个结果集。

 ctx.As.Select(a => new
    
        AId = a.Id,
        BNames = a.Bs.Select(x=>x.Name) //SubCollection
    )

但是,当执行此操作时,BNames 集合是惰性的,因此当它被枚举时,它会为每一行触发一个单独的查询。例如对于 B 的集合中的 2 个项目。

[a].[Id] AS [AId] FROM [As] AS [a]

p_executesql N'SELECT [x].[Name]
FROM [Bs] AS [x]
WHERE @_outer_Id = [x].[AId]',N'@_outer_Id int',@_outer_Id=1

p_executesql N'SELECT [x].[Name]
FROM [Bs] AS [x]
WHERE @_outer_Id = [x].[AId]',N'@_outer_Id int',@_outer_Id=2

在 EF6 中,相同的查询结果如下(如我所料):

SELECT 
[Extent1].[Id] AS [Id], 
[Project1].[C1] AS [C1], 
[Project1].[Name] AS [Name]
FROM  [dbo].[A] AS [Extent1]
LEFT OUTER JOIN  (SELECT 
    [Extent2].[Name] AS [Name], 
    [Extent2].[A_Id] AS [A_Id], 
    1 AS [C1]
    FROM [dbo].[B] AS [Extent2] ) AS [Project1] ON [Extent1].[Id] = [Project1].[A_Id]
ORDER BY [Extent1].[Id] ASC, [Project1].[C1] ASC

如何让 EF 急切加载子集合 BNames


注意:这与包含 .Include 的子实体不同,因为这是自定义投影,而不是导航属性。在此处使用.Include 会导致错误。

完整的回购代码在这里:https://gist.github.com/lukemcgregor/692834629da09b21d5a35515e86c9002

【问题讨论】:

【参考方案1】:

这在 EF Core 2.1 中使用其相关子查询优化是可能的。

您需要更新到 EF Core 2.1 并通过在子查询中添加 .ToList() 进行小修改以选择优化:

foreach (var thing in ctx.As
    .Select(a => new
    
        AId = a.Id,
        BNames = a.Bs.Select(x => x.Name).ToList()
    ))

在New features in EF Core 2.1 中查看Optimization of correlated subqueries。

【讨论】:

哈哈,讽刺的是,我什至使用投影的全部原因是因为我没有 .Aggregate 支持,直到 2.1 ;) 那么 2.1 应该在一两天内发布(不要引用我的话),然后使用 2.1.0 early access downloads 在更高版本的 EF 中不再相关。 OP 的查询只是转换为一个 SQL 查询。

以上是关于EF Core,投影子集合急切加载的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core/EF Core 2.0 升级后急切加载“类型之间未定义强制运算符”

EF Core 5.0.4 - 从核心 3.1 升级后,通过 Include() 的急切加载不起作用

停止加载子集合,如果我更新父集合

具有EF6子集合的父/子总是为空

实体框架按排序顺序加载子集合

实体框架急切加载不返回数据,延迟加载有