You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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未正确识别自增配置。确认实体类的主键配置:
    public class PowerPlantUser
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public int UserId { get; set; }
        public int PowerPlantId { get; set; }
        // 其他属性
    }
    
    不要手动给自增主键赋值,让EF自动生成。
  • 上下文跟踪残留:检查测试中依赖的服务(比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 15:24:39