-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
Bug description
Hi, I have encountered what I think is a bug when trying to update some entities that are in a many to many relationships with a favourite child.
I have 3 entities: User, Group, GroupMember.
Group also has a GroupOwner field referencing GroupMember.
I am trying to perform an update that will change list of members & change the ownership of the group.
Both operations work fine when done in isolation. But attempting to perform them in one go results in an error, and a weird one at that.
The code below throws:
System.InvalidOperationException: 'The property 'Group.Id' is defined as read-only after it has been saved,
but its value has been modified or marked as modified.'
even tho Group.Id was never touched.
group.Members = [new() { UserId = 1, GroupId = 1 }];
group.GroupOwnerId = 1;
ctx.SaveChanges();repo with reproduction code:
https://github.com/akilin/demos/tree/master/ef-many-to-many-updates
steps to reproduce locally:
git clone https://github.com/akilin/demos
cd demos
cd .\ef-many-to-many-updates\
docker-compose up -d
dotnet run
Your code
Full code in a single file can be seen here (this is a simplified reproduction of what we are observing in our real app):
https://github.com/akilin/demos/blob/master/ef-many-to-many-updates/Program.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ef_many_to_many_updates
{
static class Program
{
static void Main(string[] args)
{
// reset db
using var ctx = new AppContext();
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
// arrange
ctx.Groups.Add(new Group { Id = 1 });
ctx.Users.Add(new User { Id = 1 });
ctx.GroupMembers.Add(new GroupMember { UserId = 1, GroupId = 1 });
ctx.SaveChanges();
ctx.ChangeTracker.Clear();
// act
var group = ctx.Groups.Include(x => x.Members).Single();
//either of the 2 lines below work fine on their own. but combined - they cause the issue
group.Members = [new() { UserId = 1, GroupId = 1 }];
group.GroupOwnerId = 1;
ctx.SaveChanges();
}
}
public class User
{
public int Id { get; set; }
public ICollection<GroupMember> Groups { get; set; } = null!;
}
public class Group
{
public int Id { get; set; }
public int? GroupOwnerId { get; set; }
public GroupMember? GroupOwner { get; set; }
public ICollection<GroupMember> Members { get; set; } = null!;
}
public class GroupMember
{
public int GroupId { get; set; }
public Group Group { get; set; } = null!;
public int UserId { get; set; }
public User User { get; set; } = null!;
}
public class AppContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupMember> GroupMembers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
.UseNpgsql("Host=localhost;Database=test;Username=guest;Password=pwd");
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Group>()
.HasOne(x => x.GroupOwner)
.WithMany()
.HasForeignKey(x => new { x.Id, x.GroupOwnerId });
mb.Entity<GroupMember>().HasKey(x => new { x.GroupId, x.UserId });
mb.Entity<GroupMember>()
.HasOne(x => x.User)
.WithMany(x => x.Groups)
.HasForeignKey(x => new { x.UserId });
mb.Entity<GroupMember>()
.HasOne(x => x.Group)
.WithMany(x => x.Members)
.HasForeignKey(x => new { x.GroupId });
}
}
}Stack traces
CoreEventId.SaveChangesFailed[10000] (Microsoft.EntityFrameworkCore.Update)
An exception occurred in the database while saving changes for context type 'ef_many_to_many_updates.AppContext'.
System.InvalidOperationException: The property 'Group.Id' 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.InternalEntryBase.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<>c.<SaveChanges>b__114_0(DbContext _, ValueTuple`2 t)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
Unhandled exception. System.InvalidOperationException: The property 'Group.Id' 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.InternalEntryBase.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<>c.<SaveChanges>b__114_0(DbContext _, ValueTuple`2 t)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at ef_many_to_many_updates.Program.Main(String[] args) in C:\git\demos\ef-many-to-many-updates\Program.cs:line 31
EF Core version
10.0.0
Database provider
Npgsql.EntityFrameworkCore.PostgreSQL
Target framework
dotnet10
Operating system
win11
IDE
visual studio 2026