EF Core SaveChangesInterceptor 引发无法跟踪实体类型的实例,因为具有相同键的此类型的另一个实例已存在

EF Core SaveChangesInterceptor throws The instance of entity type cannot be tracked because another instance of this type with the same key is already

提问人:pantonis 提问时间:10/12/2023 最后编辑:Svyatoslav Danylivpantonis 更新时间:10/12/2023 访问量:46

问:

我在 EF Core 7 中有以下拦截器,它试图删除 IEnumEntity 类型的重复条目,并将相同的 ID 添加到 ChangeTracker。但是,当我调用SaveChanges时,在调用此代码时仍然收到错误The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked

var trackedEntities = context.ChangeTracker.Entries<IEnumEntity>()...

为什么我还没有得到这个,因为还没有被召唤?base.SavingChangesAsync(eventData, result, cancellationToken)

public class EnumEntitySaveInterceptor : SaveChangesInterceptor
{   
   public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData,
                                                                         InterceptionResult<int> result,
                                                                         CancellationToken cancellationToken = default)
   {
       var context = eventData.Context;

       // Get a list of all added and modified entities grouped by their type and Id
       var trackedEntities = context.ChangeTracker.Entries<IEnumEntity>()
           .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
           .GroupBy(e => new { Type = e.Entity.GetType(), Id = GetEntityId(e.Entity) });

       // Loop through each group of entities with the same Type and Id
       foreach (var group in trackedEntities)
       {
           // If there are multiple entities with the same Type and Id,
           // detach all but the first one
           var entitiesToDetach = group.Skip(1).ToList();
           foreach (var entry in entitiesToDetach)
           {
               entry.State = EntityState.Detached;
           }
       }

       return base.SavingChangesAsync(eventData, result, cancellationToken);
   }

   public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
   {
       .... same logic as SavingChangesAsync
   }


   private int GetEntityId(object entity)
   {
       var type = entity.GetType();
       var idProperty = type.GetProperty("Id");
       if (idProperty != null && idProperty.PropertyType == typeof(int))
       {
           return (int)idProperty.GetValue(entity);
       }
       throw new InvalidOperationException($"Entity type {type} does not have an Id property of type int.");
   }
}

以及异常的堆栈跟踪

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, Nullable`1 fallbackState)
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.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationCollectionChanged(InternalEntityEntry entry, INavigationBase navigationBase, IEnumerable`1 added, IEnumerable`1 removed)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationCollectionChanged(InternalEntityEntry entry, INavigationBase navigationBase, IEnumerable`1 added, IEnumerable`1 removed)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigationBase navigationBase)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.LocalDetectChanges(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager)
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges()
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.TryDetectChanges()
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.Entries[TEntity]()
at MyProject.EntityFramework.Inteceptors.EnumEntitySaveInterceptor.SavingChangesAsync(DbContextEventData eventData, InterceptionResult`1 result, CancellationToken cancellationToken) in C:\Github\........
at Microsoft.EntityFrameworkCore.Diagnostics.CoreLoggerExtensions.SaveChangesStartingAsync(IDiagnosticsLogger`1 diagnostics, DbContext context, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__64.MoveNext()

C# 实体框架核心 ef-core-7.0

评论

0赞 Svyatoslav Danyliv 10/12/2023
分析调用堆栈。您无法添加以更改具有相同类型和键的跟踪器实体。它应该很早就失败了。所以你的代码没有意义。
0赞 pantonis 10/12/2023
这就是我要做的。删除重复项。
0赞 Svyatoslav Danyliv 10/12/2023
为时已晚。 将在添加实体时抛出异常。 稍后在保存更改之前执行。ChangeTrackerSaveChangesInterceptor
0赞 pantonis 10/12/2023
清除为时已晚,但是当我调用SaveChangesAsync时,而不是在添加实体时,它会引发异常。那么我该如何拦截和删除重复的实体呢?
0赞 Svyatoslav Danyliv 10/12/2023
这里隐藏着一些东西。此代码不应失败。再次显示异常调用堆栈。

答: 暂无答案