我正在努力实现一个非常基本的" InsertOrUpdate()" DbContext上的方法。我尝试按照this post中的建议。
private static bool SaveItem<TEntity>(Object objToSave, TEntity existing = null) where TEntity : class
{
try
{
/////////////////////////////////////////
// BLOCK A
if(existing != null)
db.Set<TEntity>().Attach(existing);
/////////////////////////////////////////
db.Entry(objToSave).State = existing!=null ? EntityState.Modified : EntityState.Added;
db.SaveChanges();
} catch(Exception e)
{
Logger.Exception(e);
return false;
}
return true;
}
示例调用如下:
SaveItem(item, db.MyInstances.Where(dbItem => dbItem.ID == item.ID).FirstOrDefault());
一些定义:
class MyInstancesDbContext: DbContext { ... }
private static MyInstancesDbContext db = new MyInstancesDbContext();
据我了解,在该电话中,.Where()
将导致某种附件。所以我尝试了两个包括标有&#34; A&#34;并删除它。这两个都给我带来了同样的错误:
System.InvalidOperationException:附加类型&#39; ... MyInstance&#39;的实体。失败,因为a 另一个相同类型的实体已经具有相同的主键值。使用&#39;附加&#39;方法或将实体的状态设置为“未更改”#39;或者&#39;修改&#39;如果有的话 图中的tities具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用&#39;添加&#39;方法或者&#39; 新增&#39;实体状态跟踪图形,然后将非新实体的状态设置为“未更改”。或者&#39;修改&#39;酌情。
我发现this popular related answer此错误,用户建议使用AsNoTracking()
,但这让我觉得我没有从根本上理解某些内容或者试图忽略某些错误。
我非常感谢任何建议。
答案 0 :(得分:0)
我认为您缺少的是DbContext
跟踪实体,并且它不喜欢使用相同的主键跟踪相同类型的实体。
当你这样称呼时:
db.MyInstances.Where(dbItem => dbItem.ID == item.ID).FirstOrDefault()
如果数据库中存在主键== MyInstance
,则您已将item.ID
的实体加载到上下文中。
此行完全不需要,因为已经附加existing
- 但这可能不会导致错误。
if(existing != null) db.Set<TEntity>().Attach(existing);
问题可能在这里:
db.Entry(objToSave).State = existing != null ? EntityState.Modified : EntityState.Added;
如果existing == null
,您可能没问题,因为此行会附加objToSave
,但如果存在existing
,您就会遇到问题,因为您将会遇到问题尝试附加与objToSave
具有相同类型和主键的existing
。
相反,您可以尝试使用objToSave
设置附加实体的值:
db.Entry(existing).CurrentValues.SetValues(objToSave);
如果存在现有记录,则不会附加objToSave
。