#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. Да, пример выглядит красиво … Одно из отличий заключается в том, что я фиксирую также асинхронно, но я не думаю, что это имеет значение