如何更新关系数据库TABELS?
我正在使用Entity Framework 4.0。 我在更新数据库中的数据时遇到问题。 我得到这个例外:
ObjectStateManager中已存在具有相同键的对象。
ObjectStateManager无法使用相同的键跟踪多个对象。
我正在研究关系数据库。 以下是3个实体和那里的关系:
1 to * 1 to * Ceremony -----------> Menu ------------> CourseOption
如果我更新已包含Menus
和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); } }
我该如何摆脱这个问题? 请帮忙
编辑
今天我花了一整天的时间,我读了很多关于articals和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的新手。
更新分离的对象图可能非常困难。 (这里只有一个子集合的例子: https : //stackoverflow.com/a/5540956/270591如果你有一个盛大的子集合,那就更复杂了。)
没有通用的方法或简单的方法,比如将状态设置为Modified
(它只标记标量属性为已更改),并希望它存储所有对象图更改。
要考虑实施此类更新的详细信息:
- 用户是否在菜单添加到仪式时创建新菜单,还是仅在仪式和现有菜单之间建立关系?
- 用户是否在从仪式中删除菜单时删除现有菜单,或者他是否仅在仪式和菜单之间释放关系,但菜单应该仍然存在于数据库中(FK必须可以为空)?
- 用户可以在视图中更改(标量)菜单属性,还是只能将菜单添加到仪式中或删除它们而不更改菜单本身?
- 同样的问题适用于每个
Menu
的孙子集合CourseOptions
。
对于(相对简单)用户在您的特定仪式编辑视图中的情况…
- 可以修改仪式的标量属性
- 可以删除现有菜单,这些菜单应该在删除时从数据库中删除,并且还应删除相关的
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();
如果某些限制不适用(即用户可以在您的视图中进行更复杂的修改),代码将更加复杂。 但基本思想是从数据库中加载对象图(root和Include
和可能Include(...Select(...))
),将原始图与新的分离图进行比较并应用更改根据与新图的差异到原始图。