@@ -16,7 +16,7 @@ public class CommandBatchPreparer : ICommandBatchPreparer
16
16
{
17
17
private readonly int _minBatchSize ;
18
18
private readonly bool _sensitiveLoggingEnabled ;
19
- private readonly Multigraph < IReadOnlyModificationCommand , IAnnotatable > _modificationCommandGraph = new ( ) ;
19
+ private readonly Multigraph < IReadOnlyModificationCommand , IAnnotatable > _modificationCommandGraph ;
20
20
21
21
/// <summary>
22
22
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -29,6 +29,8 @@ public CommandBatchPreparer(CommandBatchPreparerDependencies dependencies)
29
29
_minBatchSize =
30
30
dependencies . Options . Extensions . OfType < RelationalOptionsExtension > ( ) . FirstOrDefault ( ) ? . MinBatchSize
31
31
?? 1 ;
32
+
33
+ _modificationCommandGraph = new ( dependencies . ModificationCommandComparer ) ;
32
34
Dependencies = dependencies ;
33
35
34
36
if ( dependencies . LoggingOptions . IsSensitiveDataLoggingEnabled )
@@ -57,16 +59,14 @@ public CommandBatchPreparer(CommandBatchPreparerDependencies dependencies)
57
59
{
58
60
var parameterNameGenerator = Dependencies . ParameterNameGeneratorFactory . Create ( ) ;
59
61
var commands = CreateModificationCommands ( entries , updateAdapter , parameterNameGenerator . GenerateNext ) ;
60
- var sortedCommandSets = TopologicalSort ( commands ) ;
62
+ var commandSets = TopologicalSort ( commands ) ;
61
63
62
- for ( var commandSetIndex = 0 ; commandSetIndex < sortedCommandSets . Count ; commandSetIndex ++ )
64
+ for ( var commandSetIndex = 0 ; commandSetIndex < commandSets . Count ; commandSetIndex ++ )
63
65
{
64
- var independentCommandSet = sortedCommandSets [ commandSetIndex ] ;
65
-
66
- independentCommandSet . Sort ( Dependencies . ModificationCommandComparer ) ;
66
+ var commandSet = commandSets [ commandSetIndex ] ;
67
67
68
68
var batch = Dependencies . ModificationCommandBatchFactory . Create ( ) ;
69
- foreach ( var modificationCommand in independentCommandSet )
69
+ foreach ( var modificationCommand in commandSet )
70
70
{
71
71
( modificationCommand as ModificationCommand ) ? . AssertColumnsNotInitialized ( ) ;
72
72
if ( modificationCommand . EntityState == EntityState . Modified
@@ -108,7 +108,7 @@ public CommandBatchPreparer(CommandBatchPreparerDependencies dependencies)
108
108
}
109
109
}
110
110
111
- var hasMoreCommandSets = commandSetIndex < sortedCommandSets . Count - 1 ;
111
+ var hasMoreCommandSets = commandSetIndex < commandSets . Count - 1 ;
112
112
113
113
if ( batch . ModificationCommands . Count == 1
114
114
|| batch . ModificationCommands . Count >= _minBatchSize )
@@ -295,10 +295,10 @@ private string FormatCycle(
295
295
var builder = new StringBuilder ( ) ;
296
296
for ( var i = 0 ; i < data . Count ; i ++ )
297
297
{
298
- var ( command1 , command2 , annotatables ) = data [ i ] ;
298
+ var ( command1 , command2 , edges ) = data [ i ] ;
299
299
Format ( command1 , builder ) ;
300
300
301
- switch ( annotatables . First ( ) )
301
+ switch ( edges . First ( ) )
302
302
{
303
303
case IForeignKeyConstraint foreignKey :
304
304
Format ( foreignKey , command1 , command2 , builder ) ;
@@ -536,10 +536,40 @@ private void AddForeignKeyEdges(
536
536
537
537
var dependentKeyValue = ( ( ForeignKeyConstraint ) foreignKey ) . GetRowForeignKeyValueFactory ( )
538
538
. CreateDependentValueIndex ( command ) ;
539
- if ( dependentKeyValue != null )
539
+
540
+ if ( dependentKeyValue is null || ! predecessorsMap . TryGetValue ( dependentKeyValue , out var predecessorCommands ) )
540
541
{
541
- AddMatchingPredecessorEdge (
542
- predecessorsMap , dependentKeyValue , commandGraph , command , foreignKey ) ;
542
+ continue ;
543
+ }
544
+
545
+ foreach ( var predecessor in predecessorCommands )
546
+ {
547
+ if ( predecessor != command )
548
+ {
549
+ // If we're adding/inserting a dependent where the principal key is being database-generated, then
550
+ // the dependency edge represents a batching boundary: fetch the principal database-generated
551
+ // property from the database in separate batch, in order to populate the dependent foreign key
552
+ // property in the next.
553
+ var requiresBatchingBoundary = false ;
554
+
555
+ for ( var i = 0 ; i < foreignKey . PrincipalColumns . Count ; i ++ )
556
+ {
557
+ for ( var j = 0 ; j < predecessor . Entries . Count ; j ++ )
558
+ {
559
+ var entry = predecessor . Entries [ j ] ;
560
+
561
+ if ( foreignKey . PrincipalColumns [ i ] . FindColumnMapping ( entry . EntityType ) is IColumnMapping columnMapping
562
+ && entry . IsStoreGenerated ( columnMapping . Property ) )
563
+ {
564
+ requiresBatchingBoundary = true ;
565
+ goto AfterLoop ;
566
+ }
567
+ }
568
+ }
569
+ AfterLoop :
570
+
571
+ commandGraph . AddEdge ( predecessor , command , foreignKey , requiresBatchingBoundary ) ;
572
+ }
543
573
}
544
574
}
545
575
}
@@ -623,7 +653,7 @@ private static void AddMatchingPredecessorEdge<T>(
623
653
T keyValue ,
624
654
Multigraph < IReadOnlyModificationCommand , IAnnotatable > commandGraph ,
625
655
IReadOnlyModificationCommand command ,
626
- IAnnotatable edge )
656
+ IAnnotatable edgeAnnotatable )
627
657
where T : notnull
628
658
{
629
659
if ( predecessorsMap . TryGetValue ( keyValue , out var predecessorCommands ) )
@@ -632,7 +662,7 @@ private static void AddMatchingPredecessorEdge<T>(
632
662
{
633
663
if ( predecessor != command )
634
664
{
635
- commandGraph . AddEdge ( predecessor , command , edge ) ;
665
+ commandGraph . AddEdge ( predecessor , command , edgeAnnotatable ) ;
636
666
}
637
667
}
638
668
}
0 commit comments