Ядро Entity Framework Добавляет объект с сохраненной зависимостью, что приводит к ошибке

#c# #entity-framework #entity-framework-core

#c# #entity-framework #entity-framework-core

Вопрос:

У меня есть что-то вроде этих объектов:

 class Store
{
    public int Id { get; set; }
    public List<StoreItem> Items { get; set; }
    ...
}
class Item
{
    public int Id { get; set; }
    ...
}
class StoreItem 
{
    public int ItemId { get; set; }
    public Item Item { get; set; }
    public int StoreId { get; set; }
    public Store Store { get; set; }
}
  

Когда я добавил в хранилище сохраненный элемент, а затем попытался добавить его в DbSet:

 Item item = new Item()
{
    ...
};
using (var transaction = _dbContext.Database.BeginTransaction())
{
    await _dbContext.Items.AddAsync(item); // The item is stored in store StoreItem
    await _dbContext.SaveChangesAsync();
}
...
Store store = new Store()
{
    ...
};
store.Items = new List<StoreItem>()
{
    new StoreItem()
    {
        ItemId = item.Id,
        Item = item,
        Store = store,
    }
};
using (var transaction = _dbContext.Database.BeginTransaction())
{
    await _dbContext.Stores.AddAsync(store);
    await _dbContext.SaveChangesAsync();
}
  

этот код выдает исключение, которое:

 The instance of entity type 'Item' 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.
  

Я не могу понять, как добавить новое хранилище, которое зависит от уже сохраненного элемента??

Проблема воспроизводима в PostgresSQL, вот пример проектаhttps://github.com/redradist/SO-ReproducedExample

Комментарии:

1. Откуда вы получаете store.Item ? Кажется, что он был установлен с объектом, который не отслеживается EF Core, и когда вы добавите его, store он store.Item также будет добавлен, но конфликтует с уже отслеживаемой версией Item (с тем же Id ).

2. Это действительно очень просто. Удалите первое добавление и первое сохранение. Теперь перейдите и внимательно прочитайте документацию для обоих методов

3. Но если я сначала сохраню элемент… затем через некоторое время хранилище решило добавить этот элемент, который уже сохранен в _dbContext. Элементы?? Это допустимый вариант использования

4. Да, но вы должны использовать другой контекст БД или вам нужно использовать attach.

5. Почему я не могу использовать тот же контекст БД? У меня в одном и том же DbContext разные таблицы

Ответ №1:

DbSet<T>.Add предполагается, что все объекты, которые еще не были отслежены, являются новыми.

Если вы хотите прикрепить свой элемент в качестве существующего элемента, вам следует сначала это сделать или использовать context.ChangeTracker.TrackGraph() , чтобы вы могли указать, как должен быть прикреплен каждый объект.

Комментарии:

1. Что, если не отслеживается только хранилище, но элемент прямой зависимости уже отслеживается?

Ответ №2:

Проблема была связана с тем, что в примереhttps://github.com/redradist/SO-ReproducedExample Я использовал разные объекты для одного и того же отношения «Многие ко многим»:

 var storesForItem1 = new List<StoreItemEntity>()
{
    new StoreItemEntity()
    {
        ItemId = item1Entity.Id,
        Item = item1Entity,
        Store = storeEntity,
    }
};
var storesForItem2 = new List<StoreItemEntity>()
{
    new StoreItemEntity()
    {
        ItemId = item2Entity.Id,
        Item = item2Entity,
        Store = storeEntity,
    }
};
var itemsForStore = new List<StoreItemEntity>()
{
    new StoreItemEntity()  // <-- Issue, should be used previous created StoreItemEntity for this relationship
    {
        Item = item1Entity,
        StoreId = storeEntity.Id,
        Store = storeEntity,
    },
    new StoreItemEntity()  // <-- Issue, should be used previous created StoreItemEntity for this relationship
    {
        Item = item2Entity,
        StoreId = storeEntity.Id,
        Store = storeEntity,
    }
};
  

Вы должны использовать только один экземпляр объекта с тем же ключом для конкретного объекта

Вот исправленная реализация в ветке fixed-example :

https://github.com/redradist/SO-ReproducedExample/tree/feature/fixed-example

Ответ №3:

— 1

В большинстве случаев это исключение связано с Service Registration

И, скорее всего, поскольку вы не предоставили дополнительной информации, ваш репозиторий / служба зарегистрирована как одноэлементный срок службы

-> Startup.cs services.AddSingleton<IRepository, Repository>();

Вы должны изменить его на services.AddScoped<IRepository, Repository>();

— 2

Это может происходить из-за того, что вы делитесь своим объектом с несколькими DbContext объектами.

Пожалуйста, попробуйте использовать один контекст для вашего метода.

— 3

AsNoTracking() может быть полезно, но пока вы добавляете новый объект, я не думаю, что это лучшая ситуация.

Среди всего вышеперечисленного я думаю, что источником проблемы является то, что я объяснил в 1-м пункте.

Комментарии:

1. Я делаю что-то подобное с EntityFrameworkCore.Sqlite без каких-либо служб: await _dbContext. Товары. addAsync(элемент); // Элемент хранится в хранилище StoreItem await _dbContext.SaveChangesAsync(); … ожидает _dbContext.Stores. addAsync(сохранить); ожидает _dbContext.SaveChangesAsync();

2. Спасибо за редактирование вашего ответа, теперь не могли бы вы поделиться всем своим кодом, в котором вы добавляете объекты, я должен увидеть объекты addAsync (item) — item и addAsync (store) -store и как вы их установили

3. У меня есть вопрос об обновлении, который вы можете увидеть, как я устанавливаю элемент хранилища

4. Я подготовил рабочий образец, не могли бы вы, пожалуйста, проверить его. github.com/bkarakaya01/SO-ReproducedExample

5. Да, пример выглядит красиво … Одно из отличий заключается в том, что я фиксирую также асинхронно, но я не думаю, что это имеет значение