70

当查询需要稍后在代码中访问导航属性的大表时(我明确不想使用延迟加载),什么会执行得更好.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();
4

6 回答 6

91

这取决于,尝试两个

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

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

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

快速的经验法则

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

然而,这一切只是一个起点。

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

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

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

于 2013-10-11T13:41:01.243 回答
24

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

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

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

请参阅加载相关实体

于 2013-10-11T13:24:34.753 回答
10

我同意@MichaelEdenfield 的回答,但我确实想对嵌套集合场景发表评论。您可以通过将查询从里到外来解决必须执行嵌套循环(以及对数据库的许多结果调用)的问题。

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

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

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

此外,还有一种特殊情况应与 Includes 一起考虑。如果父母和孩子之间的关系是一对一的,那么多次返回父母数据的问题就不会成为问题。

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

于 2015-02-20T08:14:16.530 回答
5

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

LoadEnableLazyLoading. 如果这个设置为false. 您仍然可以懒惰地加载您要求的实体.Load()

于 2013-10-11T13:30:30.570 回答
3

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

于 2013-10-11T13:34:11.107 回答
2

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

于 2016-01-05T16:23:47.070 回答