【问题标题】:.Include() vs .Load() performance in EntityFramework.Include() 与 .Load() 在 EntityFramework 中的性能
【发布时间】:2013-10-11 13:14:58
【问题描述】:

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

在此示例中,包含的表都只有大约 10 个条目,而员工有大约 200 个条目,并且可能会发生其中大多数将通过 include 加载,因为它们与 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();

【问题讨论】:

    标签: c# .net entity-framework


    【解决方案1】:

    视情况而定,两个都试试

    使用Include() 时,您将获得好处,即在对底层数据存储的一次调用中加载所有数据。例如,如果这是一个远程 SQL Server,这可能会大大提高性能。

    缺点Include() 查询往往真的 复杂,特别是如果您有任何过滤器(Where() 调用,例如)或尝试进行任何分组。 EF 将使用 sub-SELECTAPPLY 语句生成非常密集的嵌套查询,以获取您想要的数据。它的效率也低得多——您返回一行数据,其中包含每个可能的子对象列,因此您的顶级对象的数据将重复很多次。 (例如,具有 10 个子对象的单个父对象将产生 10 行,每行具有父对象列的相同数据。)我有单个 EF 查询变得如此复杂以至于导致死锁时与 EF 更新逻辑同时运行。

    Load() 方法更简单。每个查询都是针对单个表的单个、简单、直接的 SELECT 语句。这些在所有可能的方面都容易得多,除了你必须做很多(可能更多次)。如果您有嵌套的集合集合,您甚至可能需要遍历您的顶级对象和Load 它们的子对象。它可能会失控。

    快速的经验法则

    尽量避免在单个查询中调用超过三个Include。我发现 EF 的查询太难看,无法识别。它也符合我对 SQL Server 查询的经验法则,单个查询中最多四个 JOIN 语句效果很好,但之后是考虑重构的时候了。

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

    这取决于您的架构、环境、数据和许多其他因素。

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

    选择一个合理的“默认”模式来使用,看看它是否足够好,如果不是,优化以适应口味。

    【讨论】:

    • +1 用于考虑网络连接(如果这是远程 SQL Server)。这通常是需要考虑的重点。
    • +1 - 尝试两者。在我的用例中,Include() 用了 7 分钟,Load() 用了 1.9 秒并且使用了更少的 RAM。我加了LazyLoadingEnabled = false;
    • 优秀的答案。唯一要补充的是,我刚刚完成了这个练习,在我的(特定)案例中,我们发现两者的表现都是不可接受的。我使用 AutoMapper 的 ProjectTo (QueryableExtension) 和一个新的 v. simple DTO / POCO 来有效控制生成的 SQL EF。我的查询现在以 1/20 的时间运行。谢谢JBogard! docs.automapper.org/en/stable/Queryable-Extensions.html
    【解决方案2】:

    Include() 将作为JOIN 写入 SQL:一次数据库往返。

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

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

    Loading Related Entities

    【讨论】:

    • 您的解释是对的,但Include() 不一定是更明智的选择。根据具体的表定义和使用的数据库,包含这么多包含的查询可能会变得非常复杂,以至于它的执行情况远比使用Load() 的单独查询差。 (是的,我遇到过这种情况。)
    • @hvd 同意,这可能会发生,尽管我没有发现五个连接很多。真正的答案是:无论如何,“测量它”,因为我们对 OP 的数据库一无所知。
    • 在游戏后期,但 Load 并未明确加载相关实体。您在代码中使用显式加载,以便使用 Collection 或 Reference 关键字对相关实体进行精细控制。在序列化场景中使用 Load,例如从控制器返回 JSON,可能会导致所有实体都被递归加载。
    【解决方案3】:

    我同意@MichaelEdenfield 在他的answer 中的观点,但我确实想对嵌套集合方案发表评论。通过将查询翻过来,您可以避免必须执行嵌套循环(以及对数据库的许多结果调用)。

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

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

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

    此外,还有一种特殊情况需要考虑使用 Includes。如果父子关系是一对一的,那么多次返回父数据的问题就不是问题了。

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

    【讨论】:

      【解决方案4】:

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

      LoadEnableLazyLoading 的手动覆盖。如果这个设置为false。您仍然可以使用.Load() 懒惰地加载您请求的实体

      【讨论】:

      • 这解释了很好的区别,但问题是询问性能
      • Load 不是 EnableLazyLoading 的手动覆盖。如果将其设置为 false,则必须将显式加载与 Collection 或 Reference 关键字一起使用。见entityframeworktutorial.net/EntityFramework4.3/…
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-28
          • 1970-01-01
          • 2023-03-03
          • 2019-02-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多