EF Core DbContext воссоздает ссылку на FK - PullRequest
0 голосов
/ 18 марта 2020

Я пишу некоторые юнит-тесты в нашем ядре do tnet 3 и EF. Мы используем MSTest. У нас есть метод TestInitialize, который начинается с заполнения базы данных данными (в частности, UserRole для целей этого вопроса). Затем мы создаем объект, который ссылается на пользователя (который ссылается на UserRole)

Модели:

public class UserRole {
    public int Id { get; set; }
    public string Name { get; set; }
}

public class User {
    public int Id { get; set; }
    ...
    public UserRole { get; set; }
    public int UserRoleId { get; set; }
}

Поэтому я пытаюсь:

[TestClass]
public class MyUnitTest {
    public Task SeedBasicData(MyDbContext context) {
        // This method actually exists in another class but it's just a simple method. There's no magic behind this class or method.
        UserRole rol = new UserRole { Name = "Developer" };
        context.Add(role);
        return context.SaveChangesAsync();
    }

    [TestInitialize]
    public void Setup() {
        MyDbContext context = this.ServiceProvider.GetService<MyDbContext>();
        this.SeedBasicData(context).Wait();
        UserRole role = context.UserRoles.Single(r => r.Name == "Developer");
        User user = new User { UserRole = role };
        context.Add(user); // System.InvalidOperationException here
        context.SaveChanges();
    }

    [TestMethod]
    public async Task SomeTest() {
        ...
    }
}

Всякий раз, когда я запускаю тест , он выдает System.InvalidOperationException, когда я добавляю пользователя в контекст. Мне кажется, что он пытается создать роль, как если бы это был новый экземпляр, который еще не был сохранен.

Обратите внимание, что мы настроили наш DbContext для повсеместного использования запросов без отслеживания.

Трассировка стека выглядит следующим образом:

Initialization method MyUnitTest.Setup threw exception. System.InvalidOperationException: The instance of entity type 'UserRole' 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..
    Stack Trace:
        at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityStates(IEnumerable`1 entities, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.AddRange(IEnumerable`1 entities)
   at Microsoft.EntityFrameworkCore.DbContext.AddRange(Object[] entities)
   at <The line where I add the user>

Вопрос

Если я изменю строку, в которой я создаю экземпляр User, чтобы установить UserRoleId вместо свойства навигации (UserRole) тогда это работает. Кажется, что когда я устанавливаю свойство навигации и впервые добавляю пользователя в контекст, используя add, контекст также пытается добавить к нему UserRole, думая, что его не существует, даже если он отслеживается в трекере изменения контекста. (Я проверил, там UserRole). Почему кажется, что EntityFramework недостаточно умен, чтобы знать, что роль уже существует?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...