0

我正在使用实体框架 4.0。我在更新数据库中的数据时遇到问题。我得到了这个例外:

ObjectStateManager 中已存在具有相同键的对象。
ObjectStateManager 无法跟踪具有相同键的多个对象。

我正在研究关系数据库。以下是 3 个实体及其关系:

         1    to   *        1   to    *
Ceremony -----------> Menu ------------> CourseOption

如果我更新已经包含Menusand的仪式,一切正常CourseOptions。当我们在更新时在仪式中插入新的Menu和条目时,问题就来了。CourseOption比我得到上述例外。

更新现有仪式的 C# 代码

public HttpResponseMessage PutCeremony(int id, Ceremony ceremony)
{
    if (ModelState.IsValid && id == ceremony.Id)
    {
       db.Entry(ceremony).State = EntityState.Modified;

       try
       {
           db.SaveChanges();
       }
       catch (DbUpdateConcurrencyException)
       {
           return Request.CreateResponse(HttpStatusCode.NotFound);
       }

       return Request.CreateResponse(HttpStatusCode.OK, ceremony);
    }
    else
    {
       return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

我怎样才能摆脱这个问题?请帮忙

编辑

今天我花了一整天的时间,我读了很多文章和stackoverflow问题。我发现那once a product is fetched from the Context, the context is keeping track of it为什么不使用:

db.Entry(ceremony).State = EntityState.Modified;

我用了

db.Entry(db.Ceremonies.Find(ceremony.Id)).CurrentValues.SetValues(ceremony);

现在通过执行此更改,异常消失了,Ceremony 实体属性正在正确更改。但是仪式相关的 Menus 条目和 CourseOptions 条目没有更新。请各位大侠给点建议。我是 EF 的新手。

4

1 回答 1

1

更新分离的对象图可能非常困难。(这里只有一个子集合的示例:https ://stackoverflow.com/a/5540956/270591如果您有一个大子集合,那就更复杂了。)

没有通用方法或简单方法,例如将状态设置为Modified(它仅将标量属性标记为已更改)并希望它将存储所有对象图更改。

要考虑实施此类更新的详细信息:

  • 当菜单被添加到仪式中时用户是否创建新菜单,或者他是否仅在仪式和现有菜单之间建立关系?
  • 当菜单从仪式中删除时用户是否删除现有菜单,或者他只是释放仪式和菜单之间的关系,但菜单应该仍然存在于数据库中(那么FK必须可以为空)?
  • 用户是否可以在您的视图中更改(标量)菜单属性,或者他是否只能在仪式中添加菜单或删除它们而不更改菜单本身?
  • 同样的问题也适用于CourseOptions每个Menu.

对于(相对简单的)用户在您的特定仪式编辑视图中的情况......

  • 可以修改仪式的标量属性
  • 可以从仪式中删除现有菜单,当它们被删除时应该从数据库中删除,并且相关的CourseOptions也应该被删除,并且在数据库中启用了该关系的级联删除
  • 可以为应插入数据库的仪式创建和添加新菜单
  • 无法更改现有菜单的标量属性
  • 不能添加CourseOptions到现有的Menu,也不能从Menu
  • 可以将新CourseOptions的添加到新的Menu(并且CourseOptions应该与新菜单一起插入)
  • 不能改变现有的标量属性CourseOption

...代码如下所示:

var ceremonyInDb = db.Ceremonies.Include(c => c.Menus)
    .Single(c => c.Id == ceremony.Id);

db.Entry(ceremonyInDb).CurrentValues.SetValues(ceremony);

foreach (var menuInDb in ceremonyInDb.Menus.ToList())
    if (!ceremony.Menus.Any(m => m.Id == menuInDb.Id))
        db.Menus.Remove(menuInDb);

foreach (var menu in ceremony.Menus)
    if (!ceremonyInDb.Menus.Any(m => m.Id == menu.Id))
        ceremonyInDb.Menus.Add(menu);

db.SaveChanges();

如果某些限制不适用(即用户可以在您的视图中进行更复杂的修改),则代码会更复杂。但基本思想是从数据库中加载对象图(根和子,Include可能还有孙子Include(...Select(...))),将原始图与新的分离图进行比较,并根据新图的差异对原始图应用更改。

于 2012-10-29T18:55:55.013 回答