当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《EFCore嵌套Linq在N+1个SQL查询中选择结果》经验,为你挑选了1个好方法。

我有一个数据模型,其中'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填充.

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

System.Linq.Enumerable.WhereSelectEnumerableIteratorf__AnonymousType1>.

在迭代数据之前,不会加载数据,从而导致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/EntityFrameworkCoreGitHub回购中跟踪:问题4007

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

更新: EF版本2.1已发布,它包含一个修复程序.请参阅下面的答案.(2018年5月31日)



1> GWigWam..:

GitHub问题#4007已被标记closed-fixed为里程碑2.1.0-preview1.现在2.1预览1已经在NuGet上提供,正如这篇.NET博客文章中所讨论的那样.

版本2.1也已发布,使用以下命令安装它:

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 2.1.0

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

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'表,然后是一个带有'Top'表SELECT FROM的'Sub INNER JOIN FROM'表,基于Key-ForeignKey关系[Sub].[TopId] = [Top].[Id].然后将这些查询的结果合并到内存中.

结果是什么EF6将返回正是你所期望的,非常相似:匿名类型的数组,'a它具有性能prop1prop2在那里prop2是匿名类型的列表'b它有一个属性prop21.最重要的是,所有这些都是在.ToArray()通话后满载的!

推荐阅读
依然-狠幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有