-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Many-to-Many does not get updated for replaced entity with shared primary key #24123
Comments
@AndriySvyryd After investigating, I think this is an update pipeline issue. Here's what I think is happening. First, looking at the case that works where the EntityB has a non-PK FK to EntityA: public class EntityB
{
public int Id { get; set; }
public int EntityAId { get; set; }
public virtual EntityA EntityA { get; set; }
public virtual ICollection<EntityC> EntitiesC { get; } = new List<EntityC>();
} Looking at the change tracker before the final SaveChanges shows this:
The update pipeline generates the following changes: info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (3ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [EntitiesB]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [EntitiesB] ([EntityAId])
VALUES (@p1);
SELECT [Id]
FROM [EntitiesB]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@p2='2', @p3='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [EntityBEntityC] ([EntitiesBId], [EntitiesCId])
VALUES (@p2, @p3); Now, if we remove the non-PK FK and use the PK as the FK to EntityA, which is the case that fails: public class EntityB
{
public int Id { get; set; }
public virtual EntityA EntityA { get; set; }
public virtual ICollection<EntityC> EntitiesC { get; } = new List<EntityC>();
} In this case the change tracker looks like this:
This looks correct; in particular, the added join table entry is present. However, SaveChanges is a no-op. I'm guessing this is because the update pipeline is collapsing the reparenting. However, in doing this we also fail to insert into the join table. Full runnable repro: public class EntityA
{
public int Id { get; set; }
public virtual EntityB EntityB { get; set; }
}
public class EntityB
{
public int Id { get; set; }
//public int EntityAId { get; set; }
public virtual EntityA EntityA { get; set; }
public virtual ICollection<EntityC> EntitiesC { get; } = new List<EntityC>();
}
public class EntityC
{
public int Id { get; set; }
public virtual ICollection<EntityB> EntitiesB { get; } = new List<EntityB>();
}
public class SomeDbContext : DbContext
{
private static ILoggerFactory ContextLoggerFactory
=> LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(LogLevel.Information));
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(Your.ConnectionString)
.UseLoggerFactory(ContextLoggerFactory)
.EnableSensitiveDataLogging();
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<EntityA>()
.HasOne(e => e.EntityB)
.WithOne(e => e.EntityA)
.HasForeignKey<EntityB>(e => e.Id);
}
}
public class Program
{
public static void Main()
{
using (var context = new SomeDbContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Add(new EntityC());
context.Add(new EntityC());
context.SaveChanges();
}
using (var context = new SomeDbContext())
{
context.Add(new EntityA()
{
EntityB = new EntityB()
{
EntitiesC = { context.EntitiesC.Find(1) },
}
});
context.SaveChanges();
}
using (var context = new SomeDbContext())
{
var entityA = context.EntitiesA.Include(x => x.EntityB).First();
var entityC = context.EntitiesC.Find(2);
entityA.EntityB = new EntityB()
{
EntitiesC = { entityC }
};
context.ChangeTracker.DetectChanges();
context.SaveChanges();
}
}
} |
… instance Update snapshots on the new entry Fixes #24123
… instance Update snapshots on the new entry Fixes #24123
… instance Update snapshots on the new entry Fixes #24123
Perform navigation fixup on many-to-many skip navigations when just the join entity is removed Don't perform navigation fixup using a deleted entity when there is also an added entity with the same key value. Fixes #24123
We have the following use case:
This produces the following output:
If we uncomment and eager load EntitiesC then it will be empty:
Normal navigation properties do get updated (e.g. one to many). If we use separate primary key and foreign key for EntityB it would also work.
Include provider and version information
EF Core version:
Database provider: Microsoft.EntityFrameworkCore.Sqlite 5.0.3
Target framework: net5.0
The text was updated successfully, but these errors were encountered: