EF Core出现同Id实体已被跟踪错误的核心触发原因是什么?
错误核心触发逻辑
EF Core的DbContext实例内部维护了一个跟踪实体缓存池,该设计是为了避免同一个主键对应两个不同的实体状态,导致后续提交数据库时出现数据冲突。每次执行实体关联操作(查询、Add、Attach、Update、Remove等)时,都会执行如下校验逻辑:
- 提取待操作实体的类型和主键值
- 检查当前缓存池中是否已经存在同类型、同主键值的被跟踪实例
- 如果存在,且待操作的实体和已跟踪实例不是同一个内存引用,就会抛出该错误。
触发错误的三个必要条件
- 操作在同一个
DbContext实例的生命周期内执行 - 两次关联到
DbContext的实体属于相同类型,且主键(该报错中为Id)取值完全一致 - 第二次提交的实体是全新的内存对象,和已缓存的跟踪实例没有相同的引用地址
复现代码示例
首先定义基础实体和数据库上下文:
// 业务实体 public class Goods { public int Id { get; set; } public string Title { get; set; } public decimal Price { get; set; } } // 数据库上下文 public class ShopDbContext : DbContext { public DbSet<Goods> Goods { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("数据库连接字符串"); } }
触发错误的代码:
using var db = new ShopDbContext(); // 第一步:查询Id=1的商品,此时该实例已被加入跟踪缓存 var existGoods = db.Goods.First(g => g.Id == 1); // 第二步:手动构造一个Id同为1的新实例,和existGoods是不同的内存对象 var updateGoods = new Goods { Id = 1, Title = "修改后的商品名", Price = 99.9 }; // 第三步:尝试将新实例加入跟踪,触发校验报错 db.Goods.Update(updateGoods); db.SaveChanges();
内容的提问来源于stack exchange,提问作者Shubham Tiwari
相关产品推荐
相关产品推荐

