在EntityFramework中包含()vs .Load()的性能
当查询一个需要稍后在代码中访问导航属性的大表时(我明确不想使用延迟加载),那么执行效果会更好.Include()
还是.Load()
? 或者为什么使用这一个呢?
在这个例子中,包含的表全部只有大约10个条目,员工有大约200个条目,并且可能会发生这样的情况,其中大部分将被加载,因为它们匹配where子句。
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();
答案是“这要看,试试两个”。
使用Include()
,您可以将一次调用中的所有数据加载到底层数据存储中。 例如,如果这是一个远程SQL Server,那么这可能是一个主要的性能提升。
缺点是Include()
查询会变得非常复杂,特别是如果您有任何filter(例如, Where()
调用)或尝试执行任何分组。 EF将使用子SELECT
和APPLY
语句来生成非常严重的嵌套查询以获取所需的数据。 它的效率也低得多 – 你会得到一行数据,其中每个可能的子对象列,所以顶级对象的数据将被重复很多次。 (例如,一个具有10个子对象的父对象将生成10行,每个父对象的列的数据都是相同的)。我有单个EF查询变得如此复杂,它们在与EF同时运行时导致死锁更新逻辑。
Load()
方法要简单得多。 每个查询都是针对单个表的简单,简单的SELECT
语句。 除了你必须做很多事情(可能是多次) 之外 ,这些事情在任何可能的情况下都容易得多。 如果你有嵌套的集合集合,你甚至可能需要遍历顶层对象并Load
它们的子对象。 它可以失控。
作为一个快速的经验法则,我尽量避免在一个查询中Include
三个以上的Include
调用。 我发现EF的查询变得越来越难以承认, 它也符合我对于SQL Server查询的经验法则,一个查询中最多有四个JOIN语句工作得很好,但在那之后是时候考虑重构了。
但是,这只是一个起点。 这取决于你的模式,你的环境,你的数据和其他许多因素。 最后,你只需要尝试一下。 select一个合理的“默认”模式,看看是否足够好,如果没有,最佳品尝。
Include()
将作为JOIN
写入SQL:一个数据库往返。
每个Load()
指令都是“明确的延迟加载”所请求的实体,所以每次调用一次数据库往返。
因此,在这种情况下, Include()
很可能是更明智的select,但它取决于数据库的布局,调用这个代码的频率以及DbContext
存在时间。 你为什么不尝试两种方式和查询查询,并比较时间?
请参阅加载相关实体 。
我同意@MichaelEdenfield的回答,但我想对嵌套的collections场景发表评论。 你可以避开不必要的内部循环(以及对数据库的多次调用)。
而不是通过客户的订单集合循环下来,然后通过订单的OrderItems集合执行另一个嵌套的循环说,你可以直接查询OrderItemsfilter,如下所示。
context.OrderItems.Where(x => x.Order.CustomerId == customerId);
您将得到与嵌套循环中的负载相同的结果数据,但只需调用一次数据库即可。
此外,还有一个特殊情况应该与Includes一起考虑。 如果父母和孩子之间的关系是一对一的,那么多次返回父母数据的问题就不成问题。
我不确定如果大多数情况下没有孩子存在,会有什么效果 – 大量的空值? 稀疏的儿童在一对一的关系可能会更适合我上面概述的直接查询技术。
Include
是一个急切的加载的例子,因为你不仅加载你正在查询的实体,而且所有相关的实体。
Load
是EnableLazyLoading
的手动覆盖。 如果这个设置为false
。 你仍然可以懒惰地加载你所要求的实体.Load()
总是很难决定是否采用Eager,Explicit或者Lazy Loading。
无论如何我会推荐的是总是执行一些分析。 这是确保您的请求具有高性能的唯一方法。
有很多工具可以帮助你。 看看Julie Lerman的这篇文章 ,她列举了几种不同的分析方法。 一个简单的解决scheme是在SQL Server Management Studio中开始分析 。
不要犹豫与DBA交谈(如果你在你附近),这将有助于你理解执行计划。
你也可以看看这个演示文稿 ,我写了一个关于加载数据和性能的部分。
还有一件事要添加到这个线程。 这取决于你使用的服务器。 如果你正在使用sql server,可以使用eager加载,但对于sqlite,你将不得不使用.Load()来避免交叉加载exception,因为sqlite不能处理比一个依赖关系级别更深的一些include语句