EF6 无法将Detached状态实体附加到DBContext问题求解
解决方案
报错的核心不是你手里的实体状态,而是当前要附加的新DbContext的本地跟踪缓存中,已经存在一个和你要附加的实体主键完全相同的实例,两个实例即使数据一致,只要是不同的对象,就会触发这个冲突。你之前尝试的AsNoTracking仅作用于第一个查询上下文,不会解决第二个上下文的本地缓存冲突问题,可参考以下方案处理:
- 方案1:先清理同主键本地跟踪实例
附加前先查询当前DbContext的本地缓存是否存在同主键实体,若存在则先将其状态设置为Detached,再附加你的离线实体
代码示例:// 替换为你实体的实际主键字段名 var localEntity = db.Receipts.Local.FirstOrDefault(r => r.ReceiptId == receipt.ReceiptId); if (localEntity != null) { db.Entry(localEntity).State = System.Data.Entity.EntityState.Detached; } db.Receipts.Attach(receipt); db.Entry(receipt).State = System.Data.Entity.EntityState.Modified; - 方案2:使用AddOrUpdate方法自动处理冲突
引用System.Data.Entity.Migrations命名空间后,直接调用AddOrUpdate方法,EF会自动判断是新增还是更新,自动处理本地已存在的跟踪实例
代码示例:using System.Data.Entity.Migrations; // 自动处理同主键冲突,无需手动附加 db.Receipts.AddOrUpdate(receipt); db.SaveChanges(); - 方案3:排查导航属性的跟踪冲突
若Receipt实体包含导航属性(比如关联的订单、用户实体等),这些关联实体的主键如果已经被当前DbContext跟踪,也会触发同主键报错。你可以在附加前暂时将不需要更新的导航属性设为null,或者逐个检查导航属性的跟踪状态,提前将本地缓存中的关联实例设为Detached。 - 方案4:避开离线实体附加,手动更新现有实例
这是兼容性最高的方案,不需要处理跟踪冲突:先从当前DbContext查询出需要更新的实体,再把离线实体的修改字段手动赋值给查询到的实例,直接保存即可
代码示例:var existingReceipt = db.Receipts.Find(receipt.ReceiptId); if (existingReceipt != null) { // 按需赋值所有需要更新的字段 existingReceipt.Amount = receipt.Amount; existingReceipt.ReceiptTime = receipt.ReceiptTime; existingReceipt.Remark = receipt.Remark; // 其他字段... db.SaveChanges(); }
内容的提问来源于stack exchange,提问作者Mousa Shawar
相关产品推荐
相关产品推荐

