EntityFramework中的.Include()vs .Load()性能

时间:2013-10-11 13:14:58

标签: c# .net entity-framework

在查询需要稍后在代码中访问导航属性的大型表时(我明确地不想使用延迟加载)什么会更好地执行.Include().Load()?或者为什么要使用另一个?

在这个例子中,所包含的表只有大约10个条目,员工大约有200个条目,并且可能会发生大多数条目,因为它们与where子句匹配,因此无论如何都会加载include。

Context.Measurements.Include(m => m.Product)
                    .Include(m => m.ProductVersion)
                    .Include(m => m.Line)
                    .Include(m => m.MeasureEmployee)
                    .Include(m => m.MeasurementType)
                    .Where(m => m.MeasurementTime >= DateTime.Now.AddDays(-1))
                    .ToList();

Context.Products.Load();
Context.ProductVersions.Load();
Context.Lines.Load();
Context.Employees.Load();
Context.MeasurementType.Load();

Context.Measurements.Where(m => m.MeasurementTime >= DateTime.Now.AddDays(-1))
                    .ToList();

6 个答案:

答案 0 :(得分:78)

取决于,尝试两者

使用Include()时,您可以通过一次调用底层数据存储来加载所有数据的好处。例如,如果这是一个远程SQL Server,那么这可能是一个重大的性能提升。

缺点Include()查询往往非常 复杂,尤其是如果您有任何过滤器({{1例如,调用)或尝试进行任何分组。 EF将使用子Where()SELECT语句生成非常大量嵌套的查询,以获取所需的数据。它的效率也低得多 - 你可以在其中找到包含每个可能的子对象列的单行数据,因此顶级对象的数据将重复很多次。 (例如,具有10个子节点的单个父对象将产生10行,每个行具有父对象列的相同数据。)我有单个 EF查询变得如此复杂,导致死锁与EF更新逻辑同时运行。

APPLY方法非常简单。每个查询都是针对单个表的单个,简单,直接的Load()语句。这些更容易以各种可能的方式,除了你必须做很多(可能是很多次)。如果您有嵌套的集合集合,您甚至可能需要遍历顶级对象并SELECT其子对象。它可能会失控。

快速经验法则

尝试避免在单个查询中进行超过三次Load次调用。我发现EF的查询过于丑陋而无法识别;它也符合我对SQL Server查询的经验法则,单个查询中最多四个JOIN语句非常有效,但之后考虑重构

然而,所有这些只是一个起点。

这取决于您的架构,您的环境,数据以及许多其他因素。

最后,您只需要以各种方式尝试

选择一个合理的“默认”模式,看看它是否足够好,如果没有,请优化品尝。

答案 1 :(得分:20)

Include()将以JOIN的形式写入SQL:一个数据库往返。

每条Load() - 指令“显式加载”所请求的实体,因此每次调用一次数据库往返。

因此Include()很可能是这种情况下更明智的选择,但它取决于数据库布局,调用此代码的频率以及DbContext生存的时间。为什么不尝试两种方式并分析查询并比较时间?

请参阅Loading Related Entities

答案 2 :(得分:9)

我同意他的answer中的@MichaelEdenfield,但我确实想对嵌套馆藏场景发表评论。你可以通过内部调查来解决嵌套循环(以及对数据库产生的许多调用)。

不是通过Customer的Orders集合循环,然后通过Order的OrderItems集合执行另一个嵌套循环,而是直接使用过滤器查询OrderItems,如下所示。

context.OrderItems.Where(x => x.Order.CustomerId == customerId);

您将获得与嵌套循环中的Loads相同的结果数据,但只需调用一次数据库。

此外,还有一个特殊情况应考虑包含。如果父对象与子对象之间的关系是一对一的话,那么多次返回父数据的问题就不会成为问题。

我不确定如果大部分案例都没有孩子存在会产生什么影响 - 很多空?一对一关系中的稀疏孩子可能更适合我上面概述的直接查询技术。

答案 3 :(得分:5)

Include是一个热切加载的示例,您不仅要加载要查询的实体,还要加载所有相关实体。

LoadEnableLazyLoading的手动覆盖。如果此项设置为false。您仍然可以通过.Load()

懒洋洋地加载您要求的实体

答案 4 :(得分:1)

总是很难决定是否选择Eager,Explicit甚至Lazy Loading 无论如何,我建议总是进行一些分析。这是确保您的请求具有高效性的唯一方法 有很多工具可以帮助你。看看this article from Julie Lerman,其中列出了几种不同的分析方法。一个简单的解决方案是启动profiling in your SQL Server Management Studio 不要犹豫与DBA(如果您在附近)谈话,这将有助于您了解执行计划。
您还可以查看this presentation,其中我写了一个关于加载数据和性能的部分。

答案 5 :(得分:1)

要添加到此主题还有一件事。这取决于您使用的服务器。如果您正在使用sql server,则可以使用预先加载但是对于sqlite,您必须使用.Load()来避免交叉加载异常,因为sqlite无法处理一些比一个依赖级更深的include语句