我使用的是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"带回"表1.1"等,我想把所有相关的数据在一次命中,以尽量减少对数据库的调用次数。我知道我需要用"包括"我已经读到:
db.Table1.Where(r=>r.Id=myId).Include(x=>x.Table1.2).Include(x=>x.Table1.2)
然而,我不知道如何改变这个语句,把数据带回到"表1.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对编译感到困惑,还是因为检索太复杂,或者两者兼而有之。玩家可以使用"Include"的关卡数量是否存在限制?另外,我不确定通过指定子代的路径,然后自动检索父类,还是必须单独指定父类?
一个人可以使用& Include"的级别可能有限制?
有!我在这里解释了生成的SQL语句-
-
SELECT
子句列数为所有相关表 中所有列的总和。 - 行数是包含的子集合 中的记录之和。
这可能是从数据库返回的巨大的(长和宽)结果集。除此之外,db引擎的查询优化器很难找到一个好的查询计划。数据库将很难处理所有数据,因此命令超时也就不足为奇了。
选择
另一种方法是加载数据块。但说起来容易做起来难。在某种程度上,你已经在块中加载数据,但是这些块太小了,查询太多了(是的,N + 1)。块应该更大。没有明确的策略来做到这一点。这取决于您的表结构和数据数量。但让我试着给你指出正确的方向。
下5层
为简洁起见,假设表和关联为A
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
等应该在一个数据块中加载。
实体框架的一个很好的特性是,它通过一个被称为关系修复的过程自动连接所有加载到上下文中的实体。因此,如果单独加载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个查询。