EF - 为什么有时候急切负荷,有时是懒惰负荷

时间:2013-04-17 20:58:57

标签: entity-framework c#-4.0

我有一个共同的基本存储库搜索方法(为了清晰起见而修剪):

public class Repository<TRepository> : IRepository<TRepository>
        where TRepository : class, IEntity, new()
{    
     public virtual IQueryable<TRepository> SearchFor(Expression<Func<TRepository, bool>>   predicate, Expression<Func<TRepository, bool>> orderbylinq = null)
    {
        if (orderbylinq == null)
        {
            return DbSet.Where(predicate);
        }
        else
        {
            return DbSet.Where(predicate).OrderBy(orderbylinq);
        }
    }

}

我有一个派生的存储库类:

public class TimeDetailRepository : Repository<TimeDetail>

在我的服务层,我有一个调用SearchFor方法的类:

private TimeDetailRepository _timeDetailRepository;

        public ManageTimeDetailsAppServ()
            : base(new TimeDetailRepository())
        {
            _timeDetailRepository = new TimeDetailRepository();
        }
IQueryable<TimeDetail> timeDetails2 = _timeDetailRepository.SearchFor(
                    x => x.Id == 3214);

在这种情况下,timeDetails2已完全加载(所有相关实体都已加载)。

但是,我有另一个类(基本服务层类)进行相同的SearchFor调用,但它没有加载相关的实体:

IQueryable<TRepository> dbEntity = _repository.SearchFor(x => x.Id == result.Value);

从这两个调用中,我正在尝试创建一个利用相关实体的属性值的视图模型。为什么它会在一个案例而不是另一个案例中加载它们?这是同一个实体TimeDetail,相同的基本存储库类?

您在调试器中看到的图像。为什么要加载某些相关实体(如Facility和TimeDetailStatus)而不加载其他相关实体(如OrderHeader或Customer) enter image description here

更新

我查看了生成的查询,它们看起来很相似,即它们没有加入其他表...所以上下文可能包含来自某些相关实体的一些缓存行,这是他们是如何被包括在内的?

1 个答案:

答案 0 :(得分:4)

除非你在集合上使用.Include()方法,否则EF将进行延迟加载。既然你没有这样做,你就不会马上得到它们,但是当你打电话给它们时,你会懒得加载它们。

但是,这仅在对象上下文仍处于打开状态时有效。如果上下文消失了,那么你连接数据库的能力就会消失,所以你的属性都将为null。

我的猜测是,你所看到的差异与你如何保持环境有关。很难说,因为你没有显示你的情境,但我会从那里开始。

修改

不确定这是否与您的问题有关,但我只是想澄清延迟加载的内容。这是我最喜欢的EF示例:2个表:作者(AuthorId,AuthorName)和Book(BookId,AuthorId(FK),BookTitle)

/*  1 */ static void Main(string[] args)
/*  2 */ {
/*  3 */    Book book;
/*  4 */    using (var context = new SampleDbEntities())
/*  5 */    {
/*  6 */        book = context.Books.Single(b => b.BookId == 1);
/*  7 */    }
/*  8 */
/*  9 */    try
/* 10 */    {
/* 11 */        Console.WriteLine(book.Author.AuthorName);
/* 12 */    }
/* 13 */    catch (Exception ex)
/* 14 */    {
/* 15 */        Console.WriteLine(ex.Message);
/* 16 */    }
/* 17 */ 
/* 18 */    Console.ReadLine();
/* 19 */ }

如果按原样运行此代码,将会发生以下情况:

  • 第6行将仅查询Books表,并使用匹配记录填充book变量。数据库中永远不会触及Authors表。
  • 在第7行之后,处理了上下文,这意味着数据访问已经死亡。
  • 第11行抛出异常,试图访问Author属性。

但是,如果您使用调试器并在第7行停止,只是为了查看book变量的瞬间,它将立即查询数据库中的Authors表(当您在调试器),并填充该对象上的Author属性。这就是我所说的延迟加载 - 当您实际访问该属性时,即使在调试器中,当上下文处于活动状态时,它也会进行新的数据库调用。您现在可以继续使用代码,第11行就可以了,并且将打印作者姓名。

所以基本上,当涉及到EF时,不要相信调试器。它延迟加载数据,即使它看起来不像它。最好的办法是找出正在发生的事情是启动SQL事件探查器并查看运行的查询。