Deleting item from JSON-owned collection with nested owned relational children throws: '__synthesizedOrdinal' is read-only after it has been saved
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
When using EF Core’s JSON columns (ToJson()) with an OwnsMany collection, deleting elements works only for the first or last element in the list.
Deleting an inner element throws:
System.InvalidOperationException: The property 'XyzTermLanguageWrapper.XyzTerm__synthesizedOrdinal'
is defined as read-only after it has been saved, but its value has been modified or marked as modified.
This happens only when the JSON-stored owned entity (XyzTerm) itself contains another OwnsMany collection. Otherwise this seems to work.
### Your code
```csharp
public class AppDbContext : DbContext
{
public DbSet Dockets => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(n =>
{
n.HasKey(x => x.Id);
n.OwnsMany(e => e.Terms, t =>
{
t.ToJson(); // Stored as JSON array
t.OwnsMany(nt => nt.Texts, tt =>
{
});
});
});
}
}
public class Docket
{
public long Id { get; set; }
public List Terms { get; set; } = new();
}
public class XyzTerm
{
public int ClassNo { get; set; }
public Guid? CustomId { get; set; }
public List Texts { get; set; } = new();
}
public class XyzTermLanguageWrapper
{
public string Language { get; set; }
public string Text { get; set; }
}
// This trows
var docket = await db.Dockets.FirstAsync();
var termToDelete = docket.Terms
.First(t => t.CustomId == someInnerCustomId); // inner element, not first/last
docket.Terms.Remove(termToDelete);
await db.SaveChangesAsync(); // throws
```
### Stack traces
```text
ERROR 2025-11-19 21:27:54,334 [22 ] Microsoft.EntityFrameworkCore.Update - An exception occurred in the database while saving changes for context type 'IPSweb.EntityFrameworkCore.REDACTED'.
System.InvalidOperationException: The property 'XyzTermLanguageWrapper.XyzTerm__synthesizedOrdinal' is defined as read-only after it has been saved, but its value has been modified or marked as modified.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
System.InvalidOperationException: The property 'XyzTermLanguageWrapper.XyzTerm__synthesizedOrdinal' is defined as read-only after it has been saved, but its value has been modified or marked as modified.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
```
### Verbose output
```text
```
### EF Core version
9.0.5
### Database provider
Npgsql.EntityFrameworkCore.PostgreSQL
### Target framework
.NET 9
### Operating system
MacOS 26.1
### IDE
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.