dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.63k stars 3.15k forks source link

How do I add related Data in Microsoft.EntityFrameworkCore.InMemory ? #28269

Closed JakubK closed 2 years ago

JakubK commented 2 years ago

Hi, I'm trying to construct DbContext for the sake of automatic tests in my App. I've decided it will be better to use InMemoryDb instead of Mocks.

This is the code I wrote:

var builder = new DbContextOptionsBuilder<AppDbContext>()
    .UseInMemoryDatabase(Guid.NewGuid().ToString());

var ctx = new AppDbContext(builder.Options);

ctx.Users.AddRange(users);
ctx.SaveChanges();

ctx.Books.AddRange(books);
ctx.SaveChanges();

1 User can have N Books. Calling this code ends up with the following exception:

System.InvalidOperationException : The instance of entity type 'User' cannot be tracked because another instance with the same key value for {'Id'} is...

System.InvalidOperationException : The instance of entity type 'User' 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.
   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, 

I have noticed that when I manually change every ID to be unique between tables, then this exception is not being thrown, but I don't want to do this everywhere.

Is there any way to make EF Core ignore this and go on?

EF Core version: 7.0.0-preview.5.22302.2 Database provider: Microsoft.EntityFrameworkCore.InMemory 7.0.0-preview.5.22302.2 Target framework: .NET 6 Operating system: Windows 10 IDE: Rider

ajcvickers commented 2 years ago

@JakubK I would suggest you read the documentation on change-tracking. Beyond that, there really isn't enough information above to understand what you are attempting to do.

JakubK commented 2 years ago

@ajcvickers all I want to do is to fill this dbcontext with data created earlier. Everything is setup in these arrays being passed as an argument to AddRange method. As I mentioned earlier: This problem occurs when ids are not unique, even outside of single table's scope. Imagine having 1 user with id=1 in users list and 1 book with id=1. This book is related to user. Now adding these 2 entities causes this exception. But changing any of mentioned ids fixes it. I want to avoid this operation.

Basically this exception message complains about book with id=1 being added because there is already user with this id. It does not make sense for me.

smitpatel commented 2 years ago

Imagine having 1 user with id=1 in users list and 1 book with id=1. This book is related to user. Now adding these 2 entities causes this exception.

This is not expected behavior. Please provide a runnable repro code. If you add 2 users both of them having id=1 then it is expected exception as above.

JakubK commented 2 years ago

Okay, nevermind. Code using external library generating the lists was messing with the Navigation Property. Sorry for trouble.