DbContext 需要手动加载导航属性

DbContext requires manual load of navigation properties

我最近将我的解决方案从 EF5 升级到 EF6.1.2,并将我的数据访问层更改为使用 DbContext 而不是 ObjectContext。

我的一些单元测试失败了,我不明白为什么。旧数据访问代码示例:

public virtual T Insert(T item)
{
        if (item == null)
        {
            throw new ArgumentNullException("item", @"TaskDal.Insert");
        }

        using (var ctx = ObjectContextManager<StoreDataContext>.GetManager("StoreDataContext"))
        {
            var task = new Task();
            WriteNonKeyData(task, item);
            ctx.ObjectContext.Tasks.AddObject(task); // task.taskType null
            ctx.ObjectContext.SaveChanges(); // task.TaskType set
            return ReadData(task);
        }
}

Task 实体有一个导航 属性 TaskType。如上所述,这在 AddObject 行之后设置。

我的新代码如下所示:

public virtual T Insert(T item)
{
        if (item == null)
        {
            throw new ArgumentNullException("item", @"TaskDal.Insert");
        }

        using (var ctx = DbContextManager<StoreDataContext>.GetManager())
        {
            var task = new Task();
            WriteNonKeyData(task, item);
            ctx.DbContext.Tasks.Add(task); // task.TaskType null
            ctx.DbContext.SaveChanges(); // task.TaskType still null
            return ReadData(task);
        }
}

与旧代码不同,未设置 task.TaskType,这会导致 ReadData 出现异常。 LazyLoading 在这两个示例中都是正确的。

我可以通过手动重新加载 TaskType:

来解决这个问题
if (task.TaskType == null)
    ctx.DbContext.Entry(task).Reference(p => p.TaskType).Load();

但我更喜欢更好的解决方案,因为我确信我的代码中还有数百个其他地方需要更改,而且我很难找到所有这些地方。

Task 不会加载其导航属性,因为这些属性未实现延迟加载。查看您的 class 定义,您是否在 getter 中看到任何代码?编号

现在,看看为您的遗留代码自动创建的模型class,是否有支持延迟加载的非空getter?是的,有。

不同之处在于,使用代码优先,您的模型 classes 没有支持延迟加载的代码。仅在从数据库检索数据时由上下文创建的 proxy 对象支持延迟加载。

最简单的解决方法之一是强制 EF 为您创建代理:

    using (var ctx = DbContextManager<StoreDataContext>.GetManager())
    {
        var task = new Task();
        WriteNonKeyData(task, item);

        ctx.DbContext.Tasks.Add(task); // task.TaskType null
        ctx.DbContext.SaveChanges(); // task.TaskType still null

        // let ef create a proxy for the very same database object
        var ptask = ctx.DbContext.Tasks.First( p => p.ID == task.ID );

        // ptask.TaskType is now available as the actual type of
        // ptask is not Task but rather a TaskProxy that inherits from Task
        // and is created automatically by ef

        return ReadData(ptask);
    }