DbContext需要手动加载导航属性

时间:2015-01-13 11:54:06

标签: c# .net entity-framework entity-framework-6 dbcontext

我最近将我的解决方案从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();

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

1 个答案:

答案 0 :(得分:1)

Task将不会加载其导航属性,因为这些属性未实现为延迟加载。看一下你的类定义,你在getter中看到了什么代码吗?否。

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

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

最简单的解决方法之一是强制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);
    }