2014-01-07 115 views
1

我有实体框架生成以下类:LINQ到实体延迟加载

public partial class Branch 
{ 
    public short Id { get; set; } 
    public short CompanyId { get; set; } 
    public string Code { get; set; } 
    public string Title { get; set; } 

    public virtual Company Ts_Companies { get; set; } 
} 

我有以下的方法,这需要所有分支出来的数据库:

public Branch[] LoadBranches(int companyId, int page, int limit, string search, string sort, string sortOrder) 
    { 
     using (var dbContext = new TimeShedulerEntities()) 
     { 
      var _branches = (from ct in dbContext.Branches 
          where ct.Title.Contains(search) || ct.Code.Contains(search) 
          select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit); 
      return _branches.ToArray(); 
     } 
    } 

在我模型设计器我看到Lazy Loading设置为true,但是当我遍历分支时,属性Ts_Companies为null。此外,我得到以下异常:

型“System.ObjectDisposedException”的异常出现在 EntityFramework.dll但在用户代码中没有处理

附加信息:ObjectContext的实例已布置 和不能再用于需要连接的操作。

我忘了什么吗?

+2

您的地方,这种情况下的参考范围以外的情况下实体正在使用“使用”范围之外的分支,因此它已被处置。 – Jasen

+1

它告诉你错误信息中的错误。背景已经处理完毕。要么在你完成查询之前不要处理上下文,要么不要懒惰地加载你需要的信息。 – Servy

+0

@Servy我同意你的意思,但不应该'返回_branches.ToArray()'强制执行查询?这发生在使用语句的范围内,所以我真的不知道他可以在哪里访问处理的数据库上下文。 – evanmcdonnal

回答

3

您在函数中创建并处理了上下文,因为它位于using语句中。每个实体都知道它是从哪个上下文创建的,因此可以进行延迟加载。

当您访问Ts_Companies属性时,实体意识到它尚未加载该属性,因为它可能是导航属性,并试图请求其ObjectContext(TimeShedulerEntities)加载该属性。但是,背景已经处理完毕,因此导致了这种例外。

您需要修改您的查询,如下所示为“预加载”的Ts_Companies:

var _branches = (from ct in dbContext.Branches.Include("Ts_Companies") 
         where ct.Title.Contains(search) || ct.Code.Contains(search) 
         select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit); 

这将可能需要相当多一点的时间取决于大小加载的Ts_Companies对象和多少你最终立即恢复,但实体将停止询问其对象上下文加载Ts_Companies,因为您已经加载了它们。

附注:我发现创建和处理对象上下文在每个方法的基础上会导致实体在函数外部传递时出现问题。如果你想创建和销毁每个函数中的对象上下文,你可能希望让这个函数返回一个不是实体的东西。换句话说,有一个可以从一个实体构建的对象,并具有您需要的属性,但没有它引用该实体。在Java中,这些通常称为数据传输对象(DTO)。你失去了实体框架的读写能力,但你没有意想不到的ObjectDisposedException飞到了这个地方。

当你要求一个实体与另一个实体相关联时(例如,将实体添加到另一个实体的ICollection属性),当它们来自不同的objectcontext时,问题就出现了。这会让你很头疼,因为在执行该操作之前,你必须手动将对象附加到相同的上下文中。此外,您无法将更改保存到这些实体而无需手动将它们附加到不同的上下文。

我对我会怎么做意见:

我发现它更容易要么有一个包含所有这些数据库访问函数控制上下文的生命期的对象(也就是已经包含的对象是IDisposable和处置期间,销毁上下文),或者根本就不返回实体,并且使数据存储被读取为旧的,基本上没有任何修改能力的写入。

例如,我有我的对象(我将它称为我的数据访问对象)与一堆获取数据库对象的方法。这些方法返回实体。数据访问对象也有一个SaveChanges方法,它只是简单地调用上下文的SaveChanges方法。数据访问对象包含受保护属性中的上下文,并将其保留,直到数据访问对象本身被处置。除了数据访问对象之外,没有人可以触摸它的上下文。此时,通过手动调用“Dispose”来处理上下文。如果这是您的用例,那么数据访问对象可以在using语句中使用。

在任何情况下,它可能是最好避免将附着在它们的上下文存在,因为实体框架保持遍布在各个实体

+0

我的所有方法都是以这种方式创建的,我有一个类库,其中包含一个DbQueries类,它包含所有类似LoadBranches的方法。这种方式不正确吗?我应该把创建上下文的块放在我的网页的代码隐藏中吗? – Mivaweb

+0

这种方式非常好。问题在于在每种方法中处理上下文。如果您将'DbQueries'类设置为'IDisposable',并让所有方法都在相同的上下文中运行,只要您的DbQueries类保持不变,只要您需要这些实体即可消除ObjectDisposedException的问题。考虑一旦他们的父上下文被处置,实体将被“处置”。 –

+0

好的,我将在模型设计器中设置“延迟加载”,然后手动调用分离的方法加载公司数据。 – Mivaweb

0

但是你没有加载Ts_Companies,使用Eager Loading代替:

var _branches = dbContext.Branches 
         .Where(b => b.Title.Contains(search) || b.Code.Contains(search)) 
         .Include("Ts_Companies") 
         .OrderBy(c => c.Title) 
         .Skip((page - 1) * limit) 
         .Take(limit); 

而且我在MVC项目System.ObjectDisposedException,之前在同样的问题来了,我没有使用using块,而不是我定义我的如果我需要返回并使用一个数组(在我的视图)我使用该上下文。如果我需要只是更新一些信息,那么我已经使用using blocks.I希望这可以帮助。