DbContext 需要手动加载导航属性
本文关键字:加载 导航 属性 DbContext | 更新日期: 2023-09-27 18:37:16
我最近将解决方案从 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
不会加载其导航属性,因为这些属性不是为了延迟加载而实现的。看看你的类定义,你在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);
}