我很难在实体框架中更新实体。
场景: - 我用新的DbContext()加载一个实体.GetById(guid) - 我尝试使用扩展方法保存此实体,然后使用新的DbContext()
继承我的更新方法:
public virtual void Update(IEntity entityToUpdate)
{
var dbEntry = Context.Entry(entityToUpdate);
if (dbEntry == null) return;
if (Context.Entry(entityToUpdate).State == EntityState.Detached)
DbSet.Attach(entityToUpdate);
else
{
dbEntry.CurrentValues.SetValues(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
}
Context.SaveChanges();
}
这是我的一系列尝试。如果我使用SetValues,我被告知实体是分离的,因此不可能,如果我使用附加,我得到以下错误: 'ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。'
我显然做了一些根本错误的事情。有人可以帮我正确的方向吗?
更新:
protected void TransferClubs(object sender, EventArgs e)
{
var clubHelper = new ClubHelper();
var club = clubHelper.GetClub(new Guid("A009D0CD-71C4-42E8-88E2-037F059B12EE"));
club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);
club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);
club.Save();
}
public static bool Save(this ClubItem item)
{
var clubHelper = new ClubHelper();
clubHelper.AddOrUpdate(item);
return true;
}
public ClubItem AddOrUpdate(ClubItem item)
{
if (item.Id == Guid.Empty)
Insert(item);
else
Update(item);
return item;
}
你在我的原帖中看到的Update()方法......
答案 0 :(得分:2)
我认为如果您在TransferClubs
最后一行club.Save();
替换
clubHelper.SaveChanges();
此方法应该只需调用Context.SaveChanges();
并将更改保存到已加载的实体,即创建两个新用户,并将外键设置为已加载的club
。
老实说,更新实体的方法相当奇怪和复杂。我不知道为什么你的代码会抛出一个“ ObjectStateManager中已存在相同键的对象.ObjectStateManager无法跟踪具有相同键”异常的多个对象。但是有几个缺点和事情没有意义:
您没有处置实例化的上下文。如果ClubHelper
创建了一个新的上下文,它应该在超出范围时处理它。因此,ClubHelper
应该实现IDisposable
,而Dispose
inplementation应该调用context.Dispose()
。然后,您可以使用using
在末尾自动处理实例化对象:
protected void TransferClubs(object sender, EventArgs e)
{
using (var clubHelper = new ClubHelper())
{
// stuff...
} // Dispose called here automatically
}
这些行没有意义:
var dbEntry = Context.Entry(entityToUpdate);
if (dbEntry == null) return;
如果entityToUpdate
是对象模型的实体,则dbEntry
永远不会null
。它只能有状态Detached
。如果entityToUpdate
不是您的对象模型的实体,则Entry
会抛出异常,但不会返回null
。
if
案例没有意义:
if (Context.Entry(entityToUpdate).State == EntityState.Detached)
DbSet.Attach(entityToUpdate);
// ...
Context.SaveChanges();
Attach
将一个实体添加到状态Unchanged
中的上下文中。如果您之后只是调用SaveChanges
没有任何反应,并且将被写入数据库,因为没有任何更改。
else
案例也没有意义:
dbEntry.CurrentValues.SetValues(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
SetValues
将entityToUpdate
的属性复制到具有相同键的实体的属性,并且如果属性具有相同的名称,则已附加到上下文。如果属性值不同,则将属性标记为Modified
。如果您之后将整个实体设置为Modified
,则将所有属性标记为Modified
,这会使SetValues
变为冗余。
此外,这两行都无法帮助您创建所需的UPDATE语句,因为它们不会将关系标记为已修改,并且仅影响标量(和复杂)属性,但不影响导航属性。但更新关系 - 即在club
和两个新用户之间 - 正是您在示例中所需要的。
最后,在全新的上下文中执行更新没有意义......
var clubHelper = new ClubHelper();
clubHelper.AddOrUpdate(item);
...当您在另一个上下文之前加载并更改了实体之前的实体。实体框架已经完成了在第一个上下文中跟踪您的更改以生成所需SQL语句的所有工作。如果您在此之后创建一个新的上下文,您希望保存更改,则抛弃所有工作,并且必须从头开始告诉EF您对原始实体所做的更改。