Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The instance of entity type '**Entity**' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. #23615

Closed
mm-ryo opened this issue Dec 8, 2020 · 11 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@mm-ryo
Copy link

mm-ryo commented Dec 8, 2020

Exception details :

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

Steps:

  1. get the test entity from the database and map it to our domain model.
  2. without any changes then map it to db model
  3. call EF core api to remove it.
  4. call save changes
var testEntityModel = Mapper.Map<TestEntityModel>(DbContext.TestEntity.First());
var testEntity = Mapper.Map<TestEntity>(testEntityModel);

DbContext.TestEntity.RemoveRange(testEntity );  -- **the exception throw out.**

DbContext.SaveChanges();

Stack traces

   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.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.RemoveRange(IEnumerable`1 entities)
   at Microsoft.EntityFrameworkCore.DbContext.RemoveRange(Object[] entities)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.RemoveRange(TEntity[] entities)
   at ...........
   at Castle.Proxies.Invocations.ISolutionSharedRepository_LoadSolutions.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at ...........

Provider and Version information

EF Core version: Microsoft.EntityFrameworkCore (3.1.7)
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL (3.1.4)
Target framework: 3.1.7
Operating system: Microsoft Windows 10 Enterprise 64bit
IDE: Visual Studio 2019 16.3

@mm-ryo mm-ryo changed the title The instance of entity type '**Entity**' 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. The instance of entity type '**Entity**' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. Dec 8, 2020
@TehWardy
Copy link

TehWardy commented Dec 8, 2020

I seem to be getting this all over the place lately ...
Did something change recently to cause this to happen more often?

I've also noticed if I post an array of something to the api that then gets an entity from the db and makes changes to it more than once EF gets all confused about what's needed rough steps to produce the issue ...

  • Get an array of T from EF
  • for each in a "update set" (with overlaps)
    - Update the values on 1 item from the dbset
    - call save changes

I get this error ^

@ajcvickers
Copy link
Member

@robinNode Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@TehWardy Please open a new issue and attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@mm-ryo
Copy link
Author

mm-ryo commented Dec 9, 2020

@robinNode Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@TehWardy Please open a new issue and attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@ajcvickers
I have attached a demo. please take a look at it. thanks a lot.
EFCoreTest.zip

@roji
Copy link
Member

roji commented Dec 9, 2020

@robinNode you seem to be using AutoMapper to map an entity instance to some other type, then map back to the entity type, and then attempting to interact with change tracking using the newly-converted instance. This isn't supported, since there are now two entity instances representing the same thing (i.e. with the same key). If I remove the back-and-forth mapping, everything works fine. Possible solutions are:

  • Avoid mapping and work only on the instance returned by EF. This is the regular way to work with EF.
  • Continue mapping, but somehow make sure the changes done on the mapped instance are applied back to the instance returned by EF. I don't know if AutoMapper can help you with that.
  • After loading, detach the original instance (context.Entry(original).State = EntityState.Detached;). Because EF is no longer tracking the original instance, you can now pass it your new, mapped one. However, this is not a recommended technique; while it will work for deleting, for updating it means EF can no longer automatically determine which properties change, so you would have to specify that yourself.

/cc @ajcvickers this bears some resemblance to the immutable scenario, where the user wants to replace an already-tracked instance with another instance.

@ajcvickers
Copy link
Member

@roji Yes, I have been thinking a lot about the immutable scenario, and I think both this and that are dependent on #20124. It's then about conventions and patterns for how resolution happens in that hook. I agree that we should think about these things in a holistic way.

@roji
Copy link
Member

roji commented Dec 9, 2020

In addition to #20124, maybe some sort of Replace method on the change tracker... Are we tracking something like this already?

@Abbasalburaei
Copy link

I have got this issue and I resolved it with context.ChangeTracker.Clear() with automapper

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
@parko65
Copy link

parko65 commented Dec 11, 2023

I have got this issue and I resolved it with context.ChangeTracker.Clear() with automapper

Are you able to supply a sample snippet, I'm also mapping an incoming dto to a dbEntity before saving changes, however I'm doing all the logic within the service layer of the project and don't have direct access to the context but the repository does.

@parko65
Copy link

parko65 commented Dec 13, 2023

@robinNode

After playing around with your example code, you are indeed asking EF Core to track multiple instances of the same entity.

A simple fix is to do your mapping later on the modified entity:

using (var context = new TestContext())
{
    var testEntityModel = context.TestEntity.First();
    context.TestEntity.RemoveRange(testEntityModel); // **no exception and table is updated later**

    mapper.Map<TestEntity>(testEntityModel); // **removed storage var here just for demo purposes**
    
    context.SaveChanges();
}

@TehWardy
Copy link

Interesting. I'm sure when it comes to how I use EF i'm probably like many, massively abusing it, but I've found that as a testament to how well written it is sometimes just taking an out of the box approach can improve things.

I found that a lot of my problems went away when I instanced up a new context for literally every single row operation on the db. Pooling helped offset the perf hits but on occasion I would get an error from one context where I had somehow attached it to another.

Correct me if i'm wrong but
Tracking what entity is attached to what context is certainly not a fun task.
I think the bulk of this thread comes down to "EF did a strange thing, something unexpected was tracked on an instance"

Random thought
The change tracker: do we really need it at all?

Bear with me on this ...
So in my situation I do something like this:

  • new context -> get an entity -> dispose context right away
  • modify entity based on some event source containing the change details
  • instance new context -> attach modified entity to it -> call save changes

With context pooling and pooled factories this seems to be really fast, (like stupid fast)
What does the context give us by having just the one "request scoped" and doing all the work on the one instance that I would lose with this approach?

Another idea I had was to put a thread safety wrapper around a singleton context and just throw out all changes after every transaction with the wrapper but that felt ugly and would likely result in poorer perf since in order to scale i'm using a ton of instances at the moment. but if I could "queue" work to be done on an EF context I could probably make that fairly decent as an approach.

Side quest
I noticed that "model caching" / using a single model instance for all contexts doesn't work with query filters.
Is this a resolvable problem, I'd really like to take advantage of those gains?

@TehWardy
Copy link

I thought it best to put this in its own ticket ^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

6 participants