Search code examples
c#entity-framework-corenunitunsubscribe

Why does Entity Framework show that item is already tracked, despite of fact that it is not tracked?


I have 3 tests, and between every test, a new DbContext is created. I checked it simply run 3 test cases, all added the same object to db and there was no error. But when I run this tests:

[TestCase("[email protected]", "[email protected]", "123", "321", UserSaveResultStatus.UserAlreadyExists, Description = "Duplicated emails")]
[TestCase("[email protected]", "[email protected]", "456", "456", UserSaveResultStatus.UserWithGivenEmployeeIdAlreadyExists, Description = "Duplicated Employee Ids")]
[TestCase("[email protected]", "[email protected]", "", "", 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

I get this error:

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

I create breakpoint in place when error is thrown:

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; //STATE BEFORE ERROR IS DETACHED

    commandsContext.PowerPlantUsers.Add(personToAdd); //HERE IS AN ERROR

As we can see above also in test I create checker which shows me tracked entries. New PowerPlantUser has id = 0 and there is no this user in checker.

When I run tests separately, there is no error and all tests passed. Can someone tell me where the problem is?


Solution

  • The error implies that a different instance of a PowerPlantUser with that same ID is already tracked by the DbContext. It is also referring to an "Id" column so while you are providing a UserId and PowerPlantId I'd check to see if you are implementing some base class or such defining a common "Id" column that isn't being set up correctly as an Identity resulting in each record possibly going into the DB with a PK of "0".

    1. Check your PowerPlantUser definition for an Id column and whether it is correctly set up as an Identity column. (Can also depend on which Database you are using for testing.)

    2. Check the local tracking cache for a matching entity. If the PK is definitely the PowerPlantId + UserId, then add the following code to confirm if the Context is tracking an existing entity:

    ..

    var existingItem = commandsContext.PowerPlantUsers.Local
        .SingleOrDefault(x => x.PowerPlantId == powerPlantId && x.UserId == userId);
    

    That checks the tracking cache for the DbContext. If that returns an entity then something else is inserting and leaving a tracked instance in the DbContext.