如何正确地将已附加到上下文的实体标记为已修改?
Posted
技术标签:
【中文标题】如何正确地将已附加到上下文的实体标记为已修改?【英文标题】:How to properly mark entity already attached to context as modified? 【发布时间】:2013-05-24 17:03:28 【问题描述】:我觉得应该有一个简单的答案,但我似乎无法完全理解它。我有一个最初由MvcScaffolding tool 生成的存储库,因此它的方法如下所示:
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.");
上述两个方法在对日程进行编辑时基本上是按顺序调用的,因此它们共享同一个实体框架 DbContext 对象(通过存储库)。 然而,这最终导致了我的问题:假设除了正在编辑的现有计划之外,文件上没有匹配的计划,ValidateMatchOnFile() 方法将当前医师计划实体附加到 EF 上下文 在附加它的 InsertOrUpdate() 方法之前(通过调用 context.Entry() 的行)。然后我得到预期的 InvalidOperationException 错误:
“ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。”
我不知道解决此问题的最佳方法。我是否应该更改在 ValidateMatchOnFile() 方法中查找文件中匹配(但不同)实体的方式?我是否应该查看要插入到 InsertOrUpdate() 中的实体是否已存在于本地上下文中?关于后一种方法,我按照Ladislav's answer to this question 的方法将其插入到我的 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.");
【问题讨论】:
【参考方案1】:我会在你的存储库中添加一个方法:Any
,所以你可以这样做
physicianScheduleRepository.Any( ... your parameters ...);
在里面,方法执行
return context.PhysicianSchedules.Any(s => s.Id == physicianId
&& s. .... the other crietria);
这不会将任何实体提取到上下文中,并且只会通过线路发送一个布尔值(位)。
【讨论】:
有趣 - 在调用 InsertOrUpdate() 方法之前,我什至没有想过使用 Any() 与我所做的验证相关。我没有在我的存储库中将您提议的新方法命名为“任何”,而是使用 Exists(),我看到其他人已经根据此处的其他类似问题完成了该方法。而且效果很好 - 谢谢格特!我会用我的工作解决方案更新我的问题。以上是关于如何正确地将已附加到上下文的实体标记为已修改?的主要内容,如果未能解决你的问题,请参考以下文章