我正在按照存储库模式开发一个程序,该程序涉及使用Entity Framework访问数据库,并且遇到了问题。当我尝试在相同的上下文中两次操作相同的变量时,遇到以下异常:
System.InvalidOperationException: Attaching an entity of type
'Syn.Commons.Data.Tests.EntityFramework.Helper.DbModel.DBBox' failed because
another entity of the same type already has the same primary key value.
This can happen when using the 'Attach' method or [...]
我知道它来自这样一个事实,即在同一上下文中多次使用该实体。但是,当我尝试使用Detach()
方法时,它会丢失其所有导航属性(即所有它们成为 edit:都重置为最后一个已知值),as stated here(“分离对象”部分):null
In an independent association, the relationship information is not maintained for a detached object.
如果我要查询,我会使用AsNoTracking()
,但是由于我没有使用它们,所以不可能。这是Update()
方法:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Set<TDbType>().Attach(dbEnt);
// The following gives an error when manipulating the entity for the second time
//_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
// The following makes the entity "lose" all of its navigation properties
//DetachEntry(_context.Entry(dbEnt));
return true;
}
似乎两个解决方案相互冲突。有什么方法在保留其导航属性的同时不跟踪实体(没有显式查询)?
Attach()
。因此,该方法现在如下所示:
public bool Update(T entity)
{
// Some code to get dbEnt
_context.Entry<TDbType>(dbEnt).State = EntityState.Modified;
_context.SaveChanges();
return true;
}
答案 0 :(得分:1)
我找到了问题的答案。
实体框架从分离的实体更新时不管理关系(有关更多详细信息,请参见here和this的答案),因此必须完成手动,这有点令人遗憾,因为在添加时默认情况下会管理关系。
我找到了一个应该用来自动链接已分离实体上的更新关系的库(这正是我想要的),但是我似乎无法使其正常工作。它称为GraphDiff,以备您查看(那里还有更多,也可以搜索它们)。
感谢所有人在评论中。
entity
),并且该实体中有一个包含“许多”部分的列表:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.OneToManyList.Except( previous.OneToManyList ); // ToList() optional but very recommended
var deleted = previous.OneToManyList.Except( entity.OneToManyList ); // Same as above
// Make the appropriate changes
foreach( var item in deleted )
{
item.TheForeignKey = null; // Foreign key related with the entity
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
item.TheForeignKey = entity.ThePrimaryKey;
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
应该是这样。现在,对于多对多关系而言,它变得有些棘手。我所做的是将新实体的列表恢复为其原始值(数据库中的那个),将Attach()
实体恢复为上下文,以便实体框架可以跟踪对关系所做的更改,然后进行更改:
var previous = Find_Previous_Entity();
// Find list differences from the database entity (you may need a comparer)
var added = entity.ManyToManyList.Except( previous.ManyToManyList ); // ToList() optional but very recommended
var deleted = previous.ManyToManyList.Except( entity.ManyToManyList ); // Same as above
// Restore the current list to the value held in the database
entity.ManyToManyList.Clear();
foreach( var item in previous.ManyToManyList )
{
// You may want to insert the line below to avoid attaching both the previous entity and the current one to the context, it gets messy then
item.OtherManyToManyList.Remove(previous);
entity.ManyToManyList.Add(item);
}
// The new entity now has a copy of the previous list
//Attach the entity with the previous list
myContext.Set<ClassOfEntity>().Attach(entity);
// Entity Framework will now acknowledge the changes made in the list and automatically create the appropriate relationships
foreach( var item in deleted )
{
entity.ManyToManyList.Remove(item);
myContext.Entry(item).State = EntityState.Modified;
}
foreach( var item in added )
{
entity.ManyToManyList.Add(item);
myContext.Entry(item).State = (item.Id == 0) ? EntityState.Added : EntityState.Modified;
}
多对多部分虽然不漂亮,但确实有效。您可能已经注意到,如果您有许多对多(hehe)关系,则需要使用entity
的所有列表更新当前的previous
,然后调用{{1} },最后使用列表。
如果您还有其他方法可以做到,而不必“将实体附加到上一个列表的上下文中”,请在评论中告诉我,我们将不胜感激。无论如何,我希望它对您中的任何人都有用。