diff --git a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs index dc490cc8daf..fc4fc110ba4 100644 --- a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs +++ b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs @@ -625,14 +625,14 @@ private void AddForeignKeyEdges( } } - private static void AddMatchingPredecessorEdge( - Dictionary> predecessorsMap, - IKeyValueIndex dependentKeyValue, + private static void AddMatchingPredecessorEdge( + Dictionary> predecessorsMap, + T keyValue, Multigraph commandGraph, ModificationCommand command, IAnnotatable edge) { - if (predecessorsMap.TryGetValue(dependentKeyValue, out var predecessorCommands)) + if (predecessorsMap.TryGetValue(keyValue, out var predecessorCommands)) { foreach (var predecessor in predecessorCommands) { @@ -647,7 +647,7 @@ private static void AddMatchingPredecessorEdge( private void AddUniqueValueEdges(Multigraph commandGraph) { Dictionary> indexPredecessorsMap = null; - var keyPredecessorsMap = new Dictionary>(); + var keyPredecessorsMap = new Dictionary<(IKey, IKeyValueIndex), List>(); foreach (var command in commandGraph.Vertices) { if (command.EntityState != EntityState.Modified @@ -697,10 +697,10 @@ private void AddUniqueValueEdges(Multigraph c if (principalKeyValue != null) { - if (!keyPredecessorsMap.TryGetValue(principalKeyValue, out var predecessorCommands)) + if (!keyPredecessorsMap.TryGetValue((GetKey(key), principalKeyValue), out var predecessorCommands)) { predecessorCommands = new List(); - keyPredecessorsMap.Add(principalKeyValue, predecessorCommands); + keyPredecessorsMap.Add((GetKey(key), principalKeyValue), predecessorCommands); } predecessorCommands.Add(command); @@ -761,12 +761,18 @@ private void AddUniqueValueEdges(Multigraph c if (principalKeyValue != null) { AddMatchingPredecessorEdge( - keyPredecessorsMap, principalKeyValue, commandGraph, command, key); + keyPredecessorsMap, (GetKey(key), principalKeyValue), commandGraph, command, key); } } } } } } + + private static readonly bool _useOldKeyComparison = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue24221", out var enabled) && enabled; + + private IKey GetKey(IKey key) + => _useOldKeyComparison ? null : key; } } diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs index c74601e32e6..e86bb7f2890 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs @@ -329,6 +329,70 @@ private class BNum public string TheWalrus { get; set; } } + [ConditionalFact] + public async Task Can_add_and_remove_entities_with_keys_of_different_type() + { + using var testDatabase = SqlServerTestStore.CreateInitialized(DatabaseName); + + var options = Fixture.CreateOptions(testDatabase); + using (var context = new CompositeKeysDbContext(options)) + { + context.Database.EnsureCreatedResiliently(); + var first = new Int32CompositeKeys + { + Id1 = 1, Id2 = 2 + }; + + context.Add(first); + + var second = new Int64CompositeKeys + { + Id1 = 1, + Id2 = 2 + }; + + context.Add(second); + await context.SaveChangesAsync(); + } + + using (var context = new CompositeKeysDbContext(options)) + { + var first = context.Set().Single(); + context.Remove(first); + + var second = context.Set().Single(); + context.Remove(second); + + await context.SaveChangesAsync(); + } + } + + private class CompositeKeysDbContext : DbContext + { + public CompositeKeysDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(i => new { i.Id1, i.Id2 }); + modelBuilder.Entity().HasKey(l => new { l.Id1, l.Id2 }); + } + } + + private class Int32CompositeKeys + { + public int Id1 { get; set; } + public int Id2 { get; set; } + } + + private class Int64CompositeKeys + { + public long Id1 { get; set; } + public long Id2 { get; set; } + } + [ConditionalFact] public void Can_insert_non_owner_principal_for_owned() {