dotnet / dotnet/efcore

Owned json collection remove add new and readd old throws InvalidOperationException

Open
#37,980 2 comments 0 reactions 1 assignee Claimed by @AndriySvyryd View on GitHub
area-change-tracking area-json area-owned-entities customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Bug description

When removing and readding an owned entity from a json collection while also adding another, SaveChangesAsync will throw an InvalidOperationException.
`The instance of entity type 'OwnedType' cannot be tracked because another instance with the key value '{EntityTypeId: 237dcd95-8ed4-4239-b107-96c87ee76947, __synthesizedOrdinal: 1}' is already being tracked. When replacing owned entities, modify the properties without changing the instance or detach the previous owned entity entry first.`

### Your code

```csharp
using Microsoft.EntityFrameworkCore;

var options = new DbContextOptionsBuilder()
.EnableSensitiveDataLogging()
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDbContext;Trusted_Connection=True;")
.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information)
.Options;

using (var context = new MyDbContext(options))
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
var entity = new EntityType
{
Id = Guid.NewGuid(),
OwnedTypes = [
new OwnedType { Id = 1, Name = "OwnedType1" },
]
};
context.Add(entity);
await context.SaveChangesAsync();

var old = entity.OwnedTypes[0];
entity.OwnedTypes.Remove(old);

entity.OwnedTypes.Add(new OwnedType { Id = 2, Name = "OwnedType2" });
entity.OwnedTypes.Add(old);
await context.SaveChangesAsync(); // Throws
}

internal class MyDbContext(DbContextOptions options) : DbContext(options)
{
public DbSet EntityTypes => Set();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var type = modelBuilder.Entity();
modelBuilder.Entity().OwnsMany(x => x.OwnedTypes, cfg => cfg.ToJson());
}
}

public class EntityType
{
public Guid Id { get; set; }

public Guid PartitionKey { get; set; }

public List OwnedTypes { get; set; } = new List();
}

public class OwnedType
{
public int Id { get; set; }
public string Name { get; set; } = null!;
}
```

### Stack traces

```text
Unhandled exception. System.InvalidOperationException: The instance of entity type 'OwnedType' cannot be tracked because another instance with the key value '{EntityTypeId: 237dcd95-8ed4-4239-b107-96c87ee76947, __synthesizedOrdinal: 1}' is already being tracked. When replacing owned entities, modify the properties without changing the instance or detach the previous owned entity entry first.
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.NullableKeyIdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.UpdateIdentityMap(InternalEntityEntry entry, IKey key)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IEnumerable`1 containingPrincipalKeys, IEnumerable`1 containingForeignKeys, Object oldValue, Object newValue)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IEnumerable`1 keys, IEnumerable`1 foreignKeys, Object oldValue, Object newValue)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectKeyChange(IInternalEntry entry, IProperty property)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.PropertyChanged(IInternalEntry entry, IPropertyBase propertyBase, Boolean setModified)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.PropertyChanged(IInternalEntry entry, IPropertyBase property, Boolean setModified)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntryBase.OnPropertyChanged(IPropertyBase propertyBase, Object value, Boolean setModified)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.OnPropertyChanged(IPropertyBase propertyBase, Object value, Boolean setModified)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntryBase.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete, CurrentValueType valueType)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntryBase.SetStoreGeneratedValue(IProperty property, Object value, Boolean setModified)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.WriteJsonObject(Utf8JsonWriter writer, IInternalEntry parentEntry, IInternalEntry entry, ITypeBase structuralType, Nullable`1 ordinal)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.WriteJson(Utf8JsonWriter writer, Object value, IInternalEntry parentEntry, IPropertyBase property, Nullable`1 ordinal, Boolean isCollection, Boolean isTopLevel)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.WriteJson(Utf8JsonWriter writer, Object value, IInternalEntry parentEntry, IPropertyBase property, Nullable`1 ordinal, Boolean isCollection, Boolean isTopLevel)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.g__HandleJson|40_8(List`1 columnModifications, <>c__DisplayClass40_0&)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.GenerateColumnModifications()
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<>c.b__33_0(ModificationCommand command)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Update.ModificationCommand.get_ColumnModifications()
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.CreateCommandBatches(IEnumerable`1 commandSet, Boolean moreCommandSets, Boolean assertColumnModification, ParameterNameGenerator parameterNameGenerator)+MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList`1 entries, IUpdateAdapter updateAdapter)+MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Program.$(String[] args) in C:\Repos\ConsoleApp3\ConsoleApp3\Program.cs:line 32
at Program.(String[] args
```

### EF Core version

10.0.5

### Database provider

Microsoft.EntityFrameworkCore.SqlServer

### Target framework

.NET 10

### Operating system

W11

### IDE

VS2026

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.