Entity Framework 4.1 之三 : 贪婪加载和延迟加载

原文名称:Entity Framework 4.1: Deep Fetch vs Lazy Load (3)

原文地址:http://vincentlauzon.wordpress.com/2011/04/11/entity-framework-4-1-deep-fetch-vs-lazy-load-3/

这篇文章将讨论查询结果的加载控制。

EF4.1 允许控制对象之间的关系,当我们进行查询的时候,哪些关系的数据将会被加载到内存呢?所有相关的对象都需要吗?在一些场合可能有意义,例如,当查询的实体仅仅拥有一个相关的子实体,但是,多数情况下,你可能只需要加载部分数据,或者你喜欢的话,加载更多的数据。

默认情况下, EF4.1 仅仅加载查询中涉及的实体,但是它支持两种特性来帮助你控制加载:

  • 贪婪加载
  • 延迟加载

贪婪加载

对于下面的查询


    
    
using (var context = new MyDomainContext())
{

var orders = from o in context.Orders.Include( " OrderDetails " )
where o.CustomerName == " Mac "
select o;

这里我指定加载某些订单,就是客户名为 Mac 的客户的订单,而且希望相关的订单明细也一起加载。

你可以这样查看实际执行的 SQL 查询


    
    
Console.WriteLine(orders.ToString());

实际的 SQL 如下所示:


    
    
SELECT
[ Project1 ] . [ OrderID ] AS [ OrderID ] ,
[ Project1 ] . [ OrderTitle ] AS [ OrderTitle ] ,
[ Project1 ] . [ CustomerName ] AS [ CustomerName ] ,
[ Project1 ] . [ TransactionDate ] AS [ TransactionDate ] ,
[ Project1 ] . [ C1 ] AS [ C1 ] ,
[ Project1 ] . [ OrderDetailID ] AS [ OrderDetailID ] ,
[ Project1 ] . [ OrderID1 ] AS [ OrderID1 ] ,
[ Project1 ] . [ Cost ] AS [ Cost ] ,
[ Project1 ] . [ ItemName ] AS [ ItemName ]
FROM ( SELECT
[ Extent1 ] . [ OrderID ] AS [ OrderID ] ,
[ Extent1 ] . [ OrderTitle ] AS [ OrderTitle ] ,
[ Extent1 ] . [ CustomerName ] AS [ CustomerName ] ,
[ Extent1 ] . [ TransactionDate ] AS [ TransactionDate ] ,
[ Extent2 ] . [ OrderDetailID ] AS [ OrderDetailID ] ,
[ Extent2 ] . [ OrderID ] AS [ OrderID1 ] ,
[ Extent2 ] . [ Cost ] AS [ Cost ] ,
[ Extent2 ] . [ ItemName ] AS [ ItemName ] ,
CASE WHEN ( [ Extent2 ] . [ OrderDetailID ] IS NULL ) THEN CAST ( NULL AS int ) ELS
E 1 END AS [ C1 ]
FROM [ dbo ] . [ Orders ] AS [ Extent1 ]
LEFT OUTER JOIN [ dbo ] . [ OrderDetails ] AS [ Extent2 ] ON [ Extent1 ] . [ OrderID ]
= [ Extent2 ] . [ OrderID ]
WHERE N ' Mac ' = [ Extent1 ] . [ CustomerName ]
) AS [ Project1 ]
ORDER BY [ Project1 ] . [ OrderID ] ASC , [ Project1 ] . [ C1 ] ASC

EF4.1 生成的 SQL 不是特别易读,但是这个查询你应该能够看懂,订单明细被一起加载了。

这带来了一个关于贪婪加载的问题:查询效率。如果你执行这样的查询来获取订单和订单明细,也可以变成写出等效的查询语句,如果喜欢的话,可以更加聪明地写出返回两个查询结果的查询,一个是订单,另外一个是订单明细。这应该更加有效,因为你不需要为每一个订单明细重复订单的信息。由于一些原因,EF 并不支持。记住这一点,因为这很容易使性能变差。

无论如何,你还可以在查询中包含更多的子集。


    
    
var orders = from o in context.Orders.Include("OrderDetails").Include("Businesses")
where o.CustomerName == "Mac"
select o;

延迟加载

另外一个特性就是延迟加载,默认情况下,延迟加载被支持,如果你希望禁用它,必须显式声明,最好的位置是在 DbContext 的构造器中。


    
    
public MyDomainContext()
{
this.Configuration.LazyLoadingEnabled = false;
}

这样延迟加载就如你所愿了。当查询一个实体集的时候,相关的子实体也一并加载。

当 EF 访问实体的子实体的时候是如何工作的呢?你的集合是 POCO 的集合,所以,在访问的时候没有事件发生,EF 通过从你定义的实体派生一个动态的对象,然后覆盖你的子实体集合访问属性来实现。这就是为什么需要标记你的子实体集合属性为 virtual 的原因。


    
    
public class Order
{
public int OrderID { get; set ; }
public string OrderTitle { get; set ; }
public string CustomerName { get; set ; }
public DateTime TransactionDate { get; set ; }
public virtual List < OrderDetail > OrderDetails { get; set ; }
public virtual List < Business > Businesses { get; set ; }
}

总结一下两种加载方式的特点

贪婪加载:

  • 减少数据访问的延迟,在一次数据库的访问中返回所有的数据。
  • 你需要知道你将作什么,并且显式声明

延迟加载:

  • 非常宽容,因为只在需要的时候加载数据,不需要预先计划
  • 可能因为数据访问的延迟而降低性能,考虑到每访问父实体的子实体时,就需要访问数据库。

现在,什么时候我们应该使用哪种机制?我的建议是:除非需要循环中加载数据,我使用延迟加载。这样的话,可能会造成2-3 次服务器的查询,但是仍然是可以接受的,特别是考虑到贪婪加载的效率问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值