У меня есть две таблицы. Пользовательская таблица имеет две ссылки на другой (идентичный) объект, т.е. языки:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Language OrigLanguage { get; set; }
public Language Language { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
}
EF правильно создает таблицы:
Таблица «Пользователи»: Id, FirstName, LastName, LanguageId, OrigLanguageId
Я могу добавить пользователей с одинаковыми значениями для OrigLanguage и Language без каких-либо проблем. Как только я пытаюсь обновить существующего пользователя с одинаковыми значениями, я получаю следующее сообщение об ошибке:
InvalidOperationException: The instance of entity type 'Language' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
со следующим стеком:
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap.ThrowIdentityConflict(InternalEntityEntry entry) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap.Add(TKey key, InternalEntityEntry entry, bool updateDuplicate) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, Nullable forceStateWhenUnknownKey) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node, bool force) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, TState state, Func handleNode) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, TState state, Func handleNode) Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState, bool forceStateWhenUnknownKey) Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState) Microsoft.EntityFrameworkCore.DbContext.SetEntityState(TEntity entity, EntityState entityState) Microsoft.EntityFrameworkCore.DbContext.Attach(TEntity entity) Namespace.Data.Repository.UpdateAsync(T entity) in Repository.cs
В моем Startup.cs у меня есть следующий параметр для предотвращения отслеживания:
services.AddDbContext<DocLogDBContext>(
options => options.UseSqlServer(...)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
В моем репозитории у меня есть следующий код для обновления:
public override async Task<User> UpdateAsync(User entity)
{
// below two detachments have no effect
_context.Attach(entity.OrigLanguage).State = EntityState.Detached;
_context.Attach(entity.Language).State = EntityState.Detached;
_context.Attach(entity).State = EntityState.Modified;
await _context.SaveChangesAsync();
return entity;
}
Мой контроллер API выглядит так:
[HttpPost]
public virtual async Task<IActionResult> Edit(T model)
{
await _repository.UpdateAsync(model);
return Ok(model);
}
А это запрос от клиента:
Request URL: https://localhost:44344/api/User/Edit
Request Payload:
{
Id: 3
, FirstName: "Joe"
, LastName: "Frazer"
, Language: { Name: "english", ShortName: "en", Id: 1 }
, OrigLanguage: { Name: "english", ShortName: "en", Id: 1 }
}
Что мне здесь не хватает?
Мы можем решить это с помощью атрибута «[Foreign Key («xxxxx»)]» в классе poco. Здесь я пытаюсь исправить код, как показано ниже:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("OrigLanguage")]
public int OrigLanguageId { get; set; }
[ForeignKey("Language")]
public int LanguageId { get; set; }
public Language OrigLanguage { get; set; }
public Language Language { get; set; }
}
Здорово! Большое спасибо.