【问题标题】:Linq query with Nested Objects too slow [closed]嵌套对象的 Linq 查询太慢 [关闭]
【发布时间】:2020-10-16 08:50:31
【问题描述】:

我在代码中有一个类似于下面的 linq 查询

var results = Obj1.Obj2withinObj1.Icollection_Obj3withinObj2
  .Where(s => s.obj4withinobj3.obj5withinobj4 != null)
  .Select(s => s.obj4withinobj3.obj5withinobj4)
  .SelectMany(s => s.IenumStringwithinobj5)
  .Count();

这个查询执行得非常缓慢并且崩溃,我正在尝试修复。这确实从 sql server 返回了很多记录

问题 1.这查询 sql server 但这个 Entityframwork 或 Linqtosql 是什么。 2.代码的哪一部分运行在sql server上,哪一部分在内存中。 SelectMany 是否在内存中运行并在 sqlserver 上运行。再说一次,我自己怎么能说出来。 3. 我怎样才能使这个查询更快。数据库上的索引有帮助吗?

【问题讨论】:

  • 这是一个无效的 LINQ 查询,所以没有帮助。最好检查生成的 SQL。
  • 我正在使用 sql profiler 来查找它在后端运行的查询。它正在从所有基础表中运行一堆 Select 查询。但是我找不到一个包含所有连接的大选择语句。这可能吗?
  • 如果它是 EF 6 - 这几乎是不可能的。检查经常使用哪些导航属性(表)并通过这些属性搜索查询。使用 EF Core,您可以标记所有复杂查询 docs.microsoft.com/uk-ua/ef/core/querying/tags
  • 我对 Linq 做了一个小改动。 obj3 是 obj2 中的一个集合。现在我希望 linq 有意义?
  • 我已经编辑了你的问题,现在更清楚了。 Obj1.Obj2withinObj1.Icollection_Obj3withinObj2 这是什么?它是 IQueryable 吗?

标签: .net entity-framework linq


【解决方案1】:

您正在运行以下声明的证据:

var results = Obj1.Obj2withinObj1.Icollection_Obj3withinObj2....

并且您的分析器显示很多针对相关表的 Select 语句表明这是一个延迟加载命中。

例如,如果您有:

var obj1 = dbContext.Object1s.Where(/* ... */).Single();

然后去:

var results = obj1.Obj2withinObj1.Icollection_Obj3withinObj2....

EF 已使用Single 调用的第一条语句执行查询。任何访问 Obj1 上的集合等的尝试都将导致延迟加载操作以加载 所有 相关实体,而不是在它们之间进行查询。

选项1:处理IQueryable

var query = dbContext.Object1s.Where(/* ... */); // No Single/FirstOrDefault etc.
var results = query.Obj2withinObj1.Icollection_Obj3withinObj2....

这应该会推迟执行,直到您执行SelectMany。一般来说,我建议尽可能使用IQueryable,但你必须小心,因为表达式中的所有内容都必须能够冒泡到 SQL 才能正常工作。这意味着没有私有/静态方法,限制某些类型解释方法/强制转换,并且无法访问表达式中的未映射属性。如果您尝试执行需要在内存中执行的操作,EF6 和 EFCore 3+ 将引发异常。 EFCore 2 将自动预执行并切换到 Linq2Object(发出警告),这可能会将此问题隐藏为性能不佳的操作。

选项 2:Eager load 相关实体

var obj1 = dbContext.Object1s
   .Include(x => x.Obj2withinObj1)
       .ThenInclude(x => x.Icollection_Obj3withinObj2)
   /* ... */
   .Where(/* ... */).Single();

那么……

var results = obj1.Obj2withinObj1.Icollection_Obj3withinObj2....

通过一次性加载所有内容,您可以避免各种延迟加载调用,并使用联接执行一个查询。这最终仍然会比选项 1 更昂贵,但比延迟加载更快。

【讨论】:

  • 这非常有帮助,感谢您很好地解释了这一点。但是在找到修复程序时需要更多帮助。我已经修改了 linq 并在最后添加了一个我之前错过的 Count() 函数。对我来说,选项 2 似乎是可行的。在我的例子中,Obj1 来自一个遍历 obj1 的 Icollection 的 foreach 循环。我尝试将 foreach 循环更改为简单的 for 循环并访问 obj1,如 object1s.ElementAt(counter).Obj2withinObj1.Icollection_Obj3withinObj2.. ...Count() 剩余的 LINQ 是相同的。但探查器再次做出了多个选择语句。如何进行这种急切的加载?
  • 由于 object1s.ElementAt(counter).Include-> 不适用于收藏
  • 如果 Obj3 有链接的对象或集合,您可以链接额外的 .ThenInclude() 以预先加载它们。如果 Obj2 有其他依赖项(除了Collection<Obj3>,即还有一个Collection<Obj4>,那么您添加.Include(x => x.obj2).ThenInclude(x => x.Obj4s)(看起来Obj2 在语句中包含了两次,但这就是它解决多个依赖项的方式。)应用急切加载在 Obj1(s) 被具体化之前。最终,为了进一步缩小范围,我们需要准确了解您正在加载的内容以及您想要从中获得什么。
猜你喜欢
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多