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