Entity Framework提示实体已跟踪但实际未跟踪的问题求助
问题描述
我有3个测试用例,每个测试都会新建一个DbContext。单独运行所有测试用例,添加相同对象到数据库时无报错,但批量运行以下测试时出现跟踪冲突错误:
测试代码
[TestCase("test2@test.com", "test2@test.com", "123", "321", UserSaveResultStatus.UserAlreadyExists, Description = "Duplicated emails")] [TestCase("test3@test.com", "test4@test.com", "456", "456", UserSaveResultStatus.UserWithGivenEmployeeIdAlreadyExists, Description = "Duplicated Employee Ids")] [TestCase("test5@test.com", "test6@test.com", "", "", UserSaveResultStatus.Success, Description = "Empty Employee Ids. Should add two users")] public void ImportUsers_Should_Add_User_Only_Once_When_Email_Or_EmployeeId_Doubled_On_The_List(string email1, string email2, string employeeId1, string employeeId2, UserSaveResultStatus expectedStatus) { var emails = new string[] { email1, email2 }; var employeeIds = new string[] { employeeId1, employeeId2 }; var dto = GetUserImportDto(emails, employeeIds); var checker = dbContext.ChangeTracker .Entries() .Where(t => t.State == EntityState.Detached || t.State == EntityState.Unchanged || t.State == EntityState.Modified || t.State == EntityState.Detached || t.State == EntityState.Deleted || t.State == EntityState.Added); // ... 业务逻辑 // Asserts }
报错信息
The instance of entity type 'PowerPlantUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values
断点排查细节
在报错处设置断点,观察到:
foreach (var powerPlantId in newPowerPlantsIds) { var personToAdd = new PowerPlantUser() { UserId = userId, PowerPlantId = powerPlantId }; var state1 = commandsContext.Entry(personToAdd).State; var state2 = commandsContext.Entry(personToAdd).State; // 报错前状态为Detached commandsContext.PowerPlantUsers.Add(personToAdd); // 此处报错 }
测试中创建的checker显示跟踪条目里没有这个新的PowerPlantUser(其Id=0),但添加时仍报错。单独运行测试无问题,求排查原因。
排查原因及解决方案
- 数据库上下文未完全隔离:如果用内存数据库,批量测试时前一个测试的数据未清空,导致自增Id未重置,新实体
Id=0和遗留的已跟踪实体冲突。要确保每个测试用全新的内存数据库实例,比如初始化时用唯一名称:var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; using var context = new YourDbContext(options); - 实体主键配置错误:如果
PowerPlantUser的Id是自增主键,但代码手动设置了Id=0,或者EF未正确识别自增配置。确认实体类的主键配置:
不要手动给自增主键赋值,让EF自动生成。public class PowerPlantUser { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public int UserId { get; set; } public int PowerPlantId { get; set; } // 其他属性 } - 上下文跟踪残留:检查测试中依赖的服务(比如
commandsContext)是否为每个测试全新实例化,避免复用旧上下文。用using包裹上下文确保测试后正确释放。 - 复合主键未配置:如果
PowerPlantUser的唯一标识是UserId+PowerPlantId组合,但EF只把Id作为主键,会导致Id=0的不同实体被视为同一对象。可配置复合主键:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<PowerPlantUser>() .HasKey(p => new { p.UserId, p.PowerPlantId }); } - 启用敏感日志定位:按照报错提示开启
EnableSensitiveDataLogging,查看具体冲突的键值:var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .EnableSensitiveDataLogging() .Options;
内容的提问来源于stack exchange,提问作者Mateusz Kaleta
相关产品推荐
相关产品推荐

