使用GraphDiff删除Detached状态实体时触发DbContext异常如何解决?
问题原因
调用UpdateGraph(doc, opt => opt.OwnedCollection(x => x.ToDocumentRelationships), false)时,GraphDiff会自动将doc及其关联的DocumentRelationship实体附加到EF上下文,并将它们的状态设为Unchanged。此时你再从doc.ToDocumentRelationships中取出实体尝试标记为Deleted,EF会尝试重新附加这个实例,但上下文已经存在同一主键的跟踪实体,因此抛出主键冲突异常。
解决方法
方法1:直接操作上下文已跟踪的实体
既然UpdateGraph已经把关联实体附加到上下文,直接从上下文的本地跟踪集合里获取目标实体再标记删除即可:
using (var ctx = new MyDbContext()) { var doc = ctx.Documents .Include(x => x.ToDocumentRelationships) .Where(x=>x.Id == 1234) .AsNoTracking().FirstOrDefault(); ... ctx.UpdateGraph(doc, opt => opt.OwnedCollection(x => x.ToDocumentRelationships), false); ... // 从上下文本地缓存中查找要删除的关联实体 var docRelation = ctx.DocumentRelationships.Local.FirstOrDefault(dr => dr.Id == 目标ID); // 也可以用Find方法,会优先查本地缓存 // var docRelation = ctx.Find<DocumentRelationship>(目标ID); if(someConditions && docRelation != null) { ctx.Entry(docRelation).State = EntityState.Deleted; } ctx.SaveChanges(); }
方法2:移除集合元素后开启自动删除
如果你的删除逻辑只是移除集合中的特定元素,可以先从doc.ToDocumentRelationships里删掉目标实体,再把UpdateGraph的第三个参数设为true,让GraphDiff自动处理删除操作:
using (var ctx = new MyDbContext()) { var doc = ctx.Documents .Include(x => x.ToDocumentRelationships) .Where(x=>x.Id == 1234) .AsNoTracking().FirstOrDefault(); ... var docRelation = doc.ToDocumentRelationships.First(); if(someConditions) { doc.ToDocumentRelationships.Remove(docRelation); } // 开启自动删除,GraphDiff会删除不在当前集合中的关联实体 ctx.UpdateGraph(doc, opt => opt.OwnedCollection(x => x.ToDocumentRelationships), true); ctx.SaveChanges(); }
方法3:检查跟踪状态后再处理
如果必须从doc的集合中获取实体,可以先检查上下文是否已跟踪该实体,避免重复附加:
using (var ctx = new MyDbContext()) { var doc = ctx.Documents .Include(x => x.ToDocumentRelationships) .Where(x=>x.Id == 1234) .AsNoTracking().FirstOrDefault(); ... ctx.UpdateGraph(doc, opt => opt.OwnedCollection(x => x.ToDocumentRelationships), false); ... var docRelation = doc.ToDocumentRelationships.First(); if(someConditions) { // 查找上下文已跟踪的对应实体 var trackedEntry = ctx.ChangeTracker.Entries<DocumentRelationship>() .FirstOrDefault(e => e.Entity.Id == docRelation.Id); if(trackedEntry != null) { trackedEntry.State = EntityState.Deleted; } else { ctx.Entry(docRelation).State = EntityState.Deleted; } } ctx.SaveChanges(); }
内容的提问来源于stack exchange,提问作者Masoud
相关产品推荐
相关产品推荐

