3

我从未听说过,但人们将应用程序中的问题称为“N+1 问题”。他们正在做一个基于 Linq to SQL 的项目,并且有人发现了一个性能问题。我不太明白 - 但希望有人可以指导我。

似乎他们正在尝试获取对象列表,然后之后的 Foreach 导致了太多的数据库命中:

据我了解,源代码的第二部分仅在 forwach 中加载。

因此,加载的项目列表:

var program = programRepository.SingleOrDefault(r => r.ProgramDetailId == programDetailId);

然后,我们使用这个列表:

foreach (var phase in program.Program_Phases)
{
    phase.Program_Stages.AddRange(stages.Where(s => s.PhaseId == phase.PhaseId));
    phase.Program_Stages.ForEach(s =>
    {
         s.Program_Modules.AddRange(modules.Where(m => m.StageId == s.StageId));
    });
    phase.Program_Modules.AddRange(modules.Where(m => m.PhaseId == phase.PhaseId));
}

似乎已确定的问题是,他们希望“程序”包含它的孩子。但是当我们在查询中引用孩子时,它会重新加载程序:

program.Program_Phases

他们期望程序完全加载并在内存中,并且profilder 似乎表明该程序表,所有连接都在每个“foreach”上被调用。

这有意义吗?

(编辑:我找到这个链接: linq to sql 自动延迟加载关联实体吗? 这可能会回答我的问题,但是.. 他们使用的是更好的(其中人在......)符号,而不是这个奇怪的 (x => x....)。因此,如果此链接是答案-即,我们需要在查询中“加入”-可以这样做吗?)

4

2 回答 2

5

在 ORM 术语中,“N+1 选择问题”通常发生在您拥有具有嵌套集合属性的实体时。它是指使用延迟加载时将实体数据完全加载到内存中所需的查询次数。一般来说,查询越多,从客户端到服务器的往返次数越多,服务器处理查询所需的工作就越多,这会对性能产生巨大影响。

有多种技术可以避免这个问题。我不熟悉 Linq to SQL,但 NHibernate 支持急切获取,这在某些情况下会有所帮助。如果您不需要加载整个实体实例,那么您还可以考虑进行投影查询。另一种可能性是更改您的实体模型以避免嵌套集合。

于 2012-09-03T23:42:32.547 回答
3

对于高性能 linq,首先要弄清楚您真正关心的属性。linq 在性能方面的一个优势是您可以轻松地省去对您不会使用的数据的检索(您总是可以手动编写比 linq 做得更好的代码,但是 linq 可以很容易地做到这一点而无需创建包含数百个类的库,用于您每次遗漏的内容的轻微变体)。

你说“列表”几次。如果你不需要,不要去获取列表,只有当你多次重复使用同一个列表时。否则一次工作一项将在 99% 的时间内表现更好。

正如您所说的那样,“好”和“奇怪”的语法是说同一件事的不同方式。有些东西只能用方法和 lambdas 来表达,但另一种形式总是可以这样表达——确实是在编译之后。无论如何都会from b in someSource where b.id == 21 select b.name被编译someSource.Where(b => b.id == 21).Select(b => b.name)

您可以设置DataContext.LoadOptions以准确定义要加载哪个对象的实体(最重要的是,在不同的用途中很容易地对其进行不同的设置)。这可以解决您的 N+1 问题。

或者,您可能会发现通过自己设置适当的 id 来更新现有实体或插入新实体更容易。

即使您不觉得它更容易,在某些情况下它也可以更快。通常我会说选择更容易并忘记性能,但如果性能是这里的一个问题(你说它是),那么可以进行分析以查看两者中哪一个效果更好,值得.

于 2012-09-04T00:07:48.760 回答