如何通过LINQ Include在一个查询中检索子数据以提高性能?

时间:2016-07-03 10:05:46

标签: entity-framework linq linq-to-entities entity-framework-6

我正在使用ASP.NET 4.5,MVC5,C#,LINQ,EF6,SQL Server 2012 / SQL Azure。

我需要显着提高复杂查询的效率。本质上,任务的目的是复制具有许多子记录的“样本”记录集。我目前正在通过C#和LINQ这样做。我怀疑我在多个Foreach块中重新查询数据库,因此我引起了对数据库的多次调用。虽然每个查询都很小,但调用次数不是。它可能是200+。我相信他们称这是“N + 1”问题。

以下布局给出了关系以及查询的概念。

Table1-<Table1.1
      -<Table1.2-<Table1.2.1
                -<Table1.2.2-<Table1.2.2.1
                            -<Table1.2.2.2

我没有使用“Foreach”来恢复“Table1.1”等,而是希望在一次点击中恢复所有相关数据,以最大限度地减少对DB的调用次数。我知道我需要使用“包含”。我到目前为止:

db.Table1.Where(r=>r.Id=myId).Include(x=>x.Table1.2).Include(x=>x.Table1.2)

但是我不知道如何更改此语句以将数据恢复到“Table1.2.2.2”。这是我的问题。

提前谢谢。

编辑1

我找到了一个初步答案。

db.Table1.Include(x=>x.Table1.1) 
         .Include(x=>x.Table1.2) 
         .Include(x=>x.Table1.2.Select(y=>y.Table1.2.1)

但是我可能不需要中间线,所以以下情况可能没问题。

db.Table1.Include(x=>x.Table1.1) 
         .Include(x=>x.Table1.2.Select(y=>y.Table1.2.1)

...思想

EDIT2

我还需要降低5级。我发现这个检索超时!!这是因为EF在编译时感到困惑,还是检索太复杂或者我不确定。一个人可以使用多少级别“包含”可能有限制?另外我不确定通过指定孙子孙女的路径然后自动检索父母,或者你必须单独指定父母吗?

1 个答案:

答案 0 :(得分:2)

  

可以使用“包含”多少级别的限制?

有!正如我向here解释生成的SQL语句 -

  • SELECT子句中的列数是所有相关表中所有列的总和
  • 行的数量是所包含子集合中记录的总和

这可能是从数据库返回的巨大(长宽)结果集。除此之外,db引擎的查询优化器很难找到一个好的查询计划。数据库将很难处理所有数据,命令超时也就不足为奇了。

替代

另一种方法是以块的形式加载数据。但说起来容易做起来难。在某种程度上,您已经以块的形式加载数据,但这些块太小而查询太多(是的,N + 1)。块应该更大。如何做到这一点没有明确的策略。这取决于您的表结构和数据的数量。但是,让我试着指出你正确的方向。

  

下降5级

为了简洁起见,我们假设表和关联是A&lt; B&lt; C&lt; D&lt; E(“&lt;”表示1:n)。根查询类似于

var query = As.Where(a => a.Property == value).ToList();

[所以你不想所有 As,因为这很容易:那么你也可以加载所有孩子。]

假设您可以Include Bs没有任何问题,但包含Cs的内容已经过多了。所以查询变为:

var query = As.Where(a => a.Property == value)
              .Include(a => a.Bs).ToList();

Cs等等应该加载到一大块数据中。

实体框架的一个很好的功能是它通过一个称为 relationship fixup 的进程自动连接加载到上下文中的所有实体。因此,如果单独加载Cs,将填充其父B个对象中的集合。这样可以轻松加载所需的Cs

var cs = Cs.Where(c => c.B.A.Property == value).ToList();

(假设反向引用也是模型的一部分)

不,如果你能安全地加入Ds,我们差不多完成了:

var cs = Cs.Where(c => c.B.A.Property == value)
           .Include(c => c.Ds).ToList();

最后一个级别由:

加载
var es = Es.Where(e => e.D.C.B.A.Property == value).ToList();

这种嵌套(点)可能看起来很可怕。它将创建一个包含四个连接的查询。但是,4 Incudes的巨大差异在于现在只查询E列和行。查询结果不会爆炸。数据库引擎已针对执行连接进行了优化。

因此,这为您提供了一些处理Include级别和单独查询的句柄,直到您的配置运行良好(足够)。

最后一件事:记得关闭延迟加载。 EF会自动填充集合,但不会将它们标记为已加载。如果启用了延迟加载,则访问集合仍将触发N + 1个查询。