我觉得应该有一个简单的答案,但我似乎无法完全理解它。我有一个最初由MvcScaffolding 工具生成的存储库,因此它的方法如下所示:
public void InsertOrUpdate(PhysicianSchedule physicianSchedule)
{
if (physicianSchedule.Id == default(int)) {
// New entity
context.PhysicianSchedules.Add(physicianSchedule);
} else {
// Existing entity
context.Entry(physicianSchedule).State = EntityState.Modified;
}
}
这最初可以很好地记录医师时间表实体何时是全新的或存在但需要更新。我为该实体中的三个特定字段设置了唯一索引,因为我不能在文件中拥有两个具有相同值的不同时间表(特别是医师 ID、部门 ID 和生效日期)。
在我的 MVC 控制器中,我添加了一些模型验证,以确保有人没有添加新计划或编辑现有计划,因为这三个字段中的值与文件中的不同条目匹配:
private void ValidateMatchOnFile(PhysicianScheduleViewModel physicianScheduleViewModel)
{
PhysicianSchedule matchingScheduleOnFile = physicianScheduleRepository.Find(physicianScheduleViewModel.PhysicianId,
physicianScheduleViewModel.DepartmentId,
physicianScheduleViewModel.EffectiveDate);
if ((matchingScheduleOnFile != null) && (matchingScheduleOnFile.Id != physicianScheduleViewModel.Id))
{
ModelState.AddModelError("EffectiveDate", "There is already an effective date on file for this physician and department.");
}
}
上述两个方法在对日程进行编辑时基本上是按顺序调用的,因此它们共享同一个 Entity Framework DbContext 对象(通过存储库)。 然而,这最终导致了我的问题:假设除了正在编辑的现有计划之外,文件上没有匹配的计划,ValidateMatchOnFile() 方法在 InsertOrUpdate() 方法附加它之前将当前医师计划实体附加到 EF 上下文(通过调用context.Entry()的行)。然后我得到预期的 InvalidOperationException 错误:
“ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。”
我不知道解决此问题的最佳方法。我是否应该更改在 ValidateMatchOnFile() 方法中查找文件中匹配(但不同)实体的方式?我是否应该查看要插入到 InsertOrUpdate() 中的实体是否已存在于本地上下文中?关于后一种方法,我遵循了Ladislav 对这个问题的回答,并将其插入到我的 InsertOrUpdate() 方法中:
// Check to see if entity was already loaded into the context:
bool entityAlreadyInContext = context.Set<PhysicianSchedule>().Local
.Any(ps => ps.Id == physicianSchedule.Id);
它可以很好地确定实体是否确实存在于本地上下文中,但我不确定如何重写 IF 语句的else部分以反映实体是否已经附加到上下文中。
我正在使用 ASP.NET MVC 4 和 EF5。感谢您的任何帮助!
感谢 Gert更新我的解决方案:
在 InsertOrUpdate() 调用期间,我没有尝试在 DbContext 中使用名为 Any() 的方法,而是在我的存储库中创建了一个新方法来检查我正在寻找的内容是否存在,而实际上并未将匹配的实体附加到我的上下文中. 我将此方法添加到我的存储库中(这个名字当然不是很优雅):
public bool MatchingScheduleDifferentId(int physicianScheduleId, int physicianId, int departmentId, DateTime effectiveDate)
{
bool test = context.PhysicianSchedules.Any(ps => ps.Id != physicianScheduleId && ps.PhysicianId == physicianId && ps.DepartmentId == departmentId && ps.EffectiveDate == effectiveDate);
return test;
}
然后我将 MVC 控制器中验证方法中的逻辑简化为:
public void ValidateMatchOnFile(PhysicianScheduleViewModel physicianScheduleViewModel)
{
bool matchingScheduleOnFile = physicianScheduleRepository.MatchingScheduleDifferentId(physicianScheduleViewModel.Id,
physicianScheduleViewModel.PhysicianId,
physicianScheduleViewModel.DepartmentId,
physicianScheduleViewModel.EffectiveDate);
if (matchingScheduleOnFile == true )
{
ModelState.AddModelError("EffectiveDate", "There is already an effective date on file for this physician and department.");
}
}