我一直在努力熟悉实体框架。大部分看起来很简单,但我对使用Include方法和默认延迟加载的急切加载之间的区别感到困惑。两者似乎都加载了相关的实体,所以从表面看它们看起来像是做同样的事情。我错过了什么?
答案 0 :(得分:30)
假设您有两个具有一对多关系的实体:客户和订单,其中每个客户可以有多个订单。
在加载Customer实体时,Entity Framework允许您急切加载或延迟加载Customer's Orders集合。如果您选择急切加载Orders集合,那么当您从数据库中检索客户时,Entity Framework将生成SQL,该SQL在一个查询中检索两者客户的信息和客户的订单。但是,如果您选择延迟加载Orders集合,当您从数据库中检索客户时,Entity Framework将生成仅 提取客户信息的SQL(如果实体框架将生成单独的SQL语句,则实体框架将生成单独的SQL语句您可以在以后的代码中访问客户订单集。)
确定何时使用预先加载以及何时使用延迟加载都归结为您希望对检索的实体执行的操作。如果您知道您只需要客户的信息,那么您应该延迟加载Orders集合(以便通过仅检索客户的信息来提高SQL查询的效率)。相反,如果您知道您需要遍历客户订单,那么您应该急于加载订单(这样一旦您访问代码中的客户订单,您就可以节省额外的数据库命中率。)
P.S。使用延迟加载时要非常小心,因为它可能导致N + 1问题。例如,假设您有一个显示客户及其订单列表的页面。但是,您在获取订单时决定使用延迟加载。当您遍历Customers集合,然后遍历每个Customer's Orders时,您将为每个客户执行数据库命中,以便在其Orders集合中延迟加载。这意味着对于N个客户,您将拥有N + 1个数据库命中(1个数据库命中以加载所有客户,然后N个数据库命中以加载他们的每个订单)而不是仅仅1个数据库命中,如果您使用了预先加载(它会在一个查询中检索所有客户及其订单)。
答案 1 :(得分:17)
如果你来自SQL世界,请考虑加入。
如果您必须在网格中显示10个订单,并且下订单的客户有2个选择:
1)懒惰负荷(= 11个查询=慢性能)
EF将拍摄查询以检索订单并查询每个订单以检索客户数据。
Select * from order where order=1
+
10 x (Select * from customer where id = (order.customerId))
1)EAGER LOAD(= 1查询=高性能)
EF将拍摄单个查询,以通过JOIN检索订单和客户。
Select * from orders INNER JOIN customers on orders.customerId=customer.Id where order=1
<强> PS:强> 从db中检索对象时,对象在上下文处于活动状态时存储在缓存中。 在我使用LAZY LOAD创建的示例中,如果所有10个订单都与同一个客户相关,您将只看到2个查询,因为当您要求EF检索对象时,EF将检查该对象在缓存中,如果它发现它不会向DB发送另一个SQL查询。
答案 2 :(得分:6)
预先加载旨在解决ORM特有的N+1 Selects问题。简短的版本是这样的:如果你要直接检索一些实体并且你知道你将通过检索到的实体访问某些相关实体,那么检索所有相关实体的效率要高得多 与通过延迟加载逐步检索它们相比,在一次通过中预先进行。
答案 3 :(得分:0)
一个重要的问题是序列化。如果您正在处理序列化对象,Microsoft建议不要使用默认的延迟加载。序列化导致调用所有相关属性,这可以启动被查询的相关实体的连锁反应。如果您从控制器返回JSON数据,这确实会发挥作用。 JSON数据显然是序列化的。您要么想通过Eager立即返回数据,要么在上下文中关闭延迟加载并使用Explicit Lazy loading。