Linq以实体懒加载

时间:2014-01-07 19:24:46

标签: c# linq entity-framework

我有实体框架生成的以下类:

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 个答案:

答案 0 :(得分: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属性)来自不同的objectcontexts时,问题就出现了。这将为您带来麻烦,因为您必须在执行该操作之前手动将对象附加到相同的上下文。此外,您无法将更改保存到这些实体,而无需手动将它们附加到不同的上下文。

我对如何做的意见

我发现让一个包含所有这些数据库访问函数的对象更容易控制上下文的生命周期(即让你的包含对象是IDisposable并在处理期间,破坏上下文)或者根本不返回实体并且数据存储区是旧的,写新的,基本上没有任何修改能力。

例如,我有一些用于获取数据库对象的方法(我将其称为我的数据访问对象)。这些方法返回实体。数据访问对象还有一个SaveChanges方法,它只调用上下文的SaveChanges方法。数据访问对象包含受保护属性中的上下文,并将其保留,直到数据访问对象本身被释放为止。除了数据访问对象之外,没有人可以触及其上下文。此时,通过手动调用'Dispose'来处理上下文。然后,如果这是您的用例,则可以在using语句中使用数据访问对象。

在任何情况下,最好避免传递附加到其上下文所在范围之外的上下文的实体,因为实体框架会在各个实体中的所有位置保持对该上下文的引用

答案 1 :(得分: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块。我希望这会有所帮助。