如何正确标记已附加到上下文的实体已修改?

时间:2013-05-24 17:03:28

标签: c# asp.net-mvc entity-framework repository

我觉得应该有一个简单的答案,但我似乎无法完全理解它。我有一个最初由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和生效日期)。 / p>

在我的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()方法将当前医生日程表实体附加到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 个答案:

答案 0 :(得分:1)

我会在您的存储库中添加一个方法:Any,以便您可以

physicianScheduleRepository.Any( ... your parameters ...);

在里面,该方法执行

return context.PhysicianSchedules.Any(s => s.Id == physicianId 
                                        && s. .... the other crietria);

这不会将任何实体提取到上下文中,只会在该行上发送一个布尔值(位)。