Skip to content

Commit 99f03c7

Browse files
authored
Use unmapped FKs for topological order when propagating store-generated values
Fixes #28654
1 parent dce042c commit 99f03c7

File tree

86 files changed

+2083
-894
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+2083
-894
lines changed

src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,22 +1375,13 @@ public static IEnumerable<IReadOnlyForeignKey> FindRowInternalForeignKeys(
13751375

13761376
foreach (var foreignKey in entityType.GetForeignKeys())
13771377
{
1378-
if (!foreignKey.PrincipalKey.IsPrimaryKey()
1379-
|| foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType)
1380-
|| !foreignKey.Properties.SequenceEqual(primaryKey.Properties)
1381-
|| !IsMapped(foreignKey, storeObject))
1378+
if (!foreignKey.IsRowInternal(storeObject))
13821379
{
13831380
continue;
13841381
}
13851382

13861383
yield return foreignKey;
13871384
}
1388-
1389-
static bool IsMapped(IReadOnlyForeignKey foreignKey, StoreObjectIdentifier storeObject)
1390-
=> (StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, storeObject.StoreObjectType) == storeObject
1391-
|| foreignKey.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject))
1392-
&& (StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, storeObject.StoreObjectType) == storeObject
1393-
|| foreignKey.PrincipalEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject));
13941385
}
13951386

13961387
/// <summary>

src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,40 @@ public static IEnumerable<IForeignKeyConstraint> GetMappedConstraints(this IFore
191191
return rootForeignKey == foreignKey ? null : rootForeignKey;
192192
}
193193

194+
/// <summary>
195+
/// Returns a value indicating whether this foreign key is between two entity types
196+
/// sharing the same table-like store object.
197+
/// </summary>
198+
/// <param name="foreignKey">The foreign key.</param>
199+
/// <param name="storeObject">The identifier of the store object.</param>
200+
public static bool IsRowInternal(
201+
this IReadOnlyForeignKey foreignKey,
202+
StoreObjectIdentifier storeObject)
203+
{
204+
var entityType = foreignKey.DeclaringEntityType;
205+
var primaryKey = entityType.FindPrimaryKey();
206+
if (primaryKey == null || entityType.IsMappedToJson())
207+
{
208+
return false;
209+
}
210+
211+
if (!foreignKey.PrincipalKey.IsPrimaryKey()
212+
|| foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType)
213+
|| !foreignKey.Properties.SequenceEqual(primaryKey.Properties)
214+
|| !IsMapped(foreignKey, storeObject))
215+
{
216+
return false;
217+
}
218+
219+
return true;
220+
221+
static bool IsMapped(IReadOnlyForeignKey foreignKey, StoreObjectIdentifier storeObject)
222+
=> (StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, storeObject.StoreObjectType) == storeObject
223+
|| foreignKey.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject))
224+
&& (StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, storeObject.StoreObjectType) == storeObject
225+
|| foreignKey.PrincipalEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject));
226+
}
227+
194228
/// <summary>
195229
/// <para>
196230
/// Finds the first <see cref="IMutableForeignKey" /> that is mapped to the same constraint in a shared table-like object.

src/EFCore.Relational/Metadata/Builders/StoredProcedureParameterBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public StoredProcedureParameterBuilder(
3535
/// </summary>
3636
public virtual IMutableStoredProcedureParameter Metadata
3737
=> Builder.Metadata;
38-
38+
3939
/// <summary>
4040
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
4141
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

src/EFCore.Relational/Metadata/IConventionStoredProcedureParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public interface IConventionStoredProcedureParameter : IReadOnlyStoredProcedureP
2020
/// </summary>
2121
/// <exception cref="InvalidOperationException">If the stored procedure parameter has been removed from the model.</exception>
2222
new IConventionStoredProcedureParameterBuilder Builder { get; }
23-
23+
2424
/// <summary>
2525
/// Sets the parameter name.
2626
/// </summary>
@@ -33,7 +33,7 @@ public interface IConventionStoredProcedureParameter : IReadOnlyStoredProcedureP
3333
/// </summary>
3434
/// <returns>The configuration source for <see cref="IReadOnlyStoredProcedureParameter.Name" />.</returns>
3535
ConfigurationSource? GetNameConfigurationSource();
36-
36+
3737
/// <summary>
3838
/// Sets the direction of the parameter.
3939
/// </summary>

src/EFCore.Relational/Metadata/IMutableStoredProcedureParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ public interface IMutableStoredProcedureParameter : IReadOnlyStoredProcedurePara
1414
/// Gets the stored procedure to which this parameter belongs.
1515
/// </summary>
1616
new IMutableStoredProcedure StoredProcedure { get; }
17-
17+
1818
/// <summary>
1919
/// Gets or sets the parameter name.
2020
/// </summary>
2121
new string Name { get; set; }
22-
22+
2323
/// <summary>
2424
/// Gets or sets the direction of the parameter.
2525
/// </summary>

src/EFCore.Relational/Metadata/IMutableStoredProcedureResultColumn.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface IMutableStoredProcedureResultColumn : IReadOnlyStoredProcedureR
1212
/// Gets the stored procedure to which this result column belongs.
1313
/// </summary>
1414
new IMutableStoredProcedure StoredProcedure { get; }
15-
15+
1616
/// <summary>
1717
/// Gets or sets the result column name.
1818
/// </summary>

src/EFCore.Relational/Metadata/IReadOnlyStoredProcedure.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public interface IReadOnlyStoredProcedure : IReadOnlyAnnotatable
5151
{
5252
return StoreObjectIdentifier.DeleteStoredProcedure(name, Schema);
5353
}
54-
54+
5555
if (EntityType.GetUpdateStoredProcedure() == this)
5656
{
5757
return StoreObjectIdentifier.UpdateStoredProcedure(name, Schema);

src/EFCore.Relational/Metadata/IReadOnlyStoredProcedureParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface IReadOnlyStoredProcedureParameter : IReadOnlyAnnotatable
1515
/// Gets the stored procedure to which this parameter belongs.
1616
/// </summary>
1717
IReadOnlyStoredProcedure StoredProcedure { get; }
18-
18+
1919
/// <summary>
2020
/// Gets the parameter name.
2121
/// </summary>
@@ -25,7 +25,7 @@ public interface IReadOnlyStoredProcedureParameter : IReadOnlyAnnotatable
2525
/// Gets the name of property mapped to this parameter.
2626
/// </summary>
2727
string? PropertyName { get; }
28-
28+
2929
/// <summary>
3030
/// Gets the direction of the parameter.
3131
/// </summary>

src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public MigrationsModelDiffer(
8484
/// any release. You should only use it directly in your code with extreme caution and knowing that
8585
/// doing so can result in application failures when updating to a new Entity Framework Core release.
8686
/// </summary>
87-
protected virtual IRowIdentityMapFactory RowIdentityMapFactory { get; }
87+
protected virtual IRowIdentityMapFactory RowIdentityMapFactory { get; }
8888

8989
/// <summary>
9090
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -702,7 +702,7 @@ private static IEnumerable<IColumn> GetSortedColumns(ITable table)
702702

703703
Check.DebugAssert(columns.Count == 0, "columns is not empty");
704704

705-
// issue #28539
705+
// issue #28539
706706
// ideally we should inject JSON column in the place corresponding to the navigation that maps to it in the clr type
707707
var jsonColumns = table.Columns.Where(x => x is JsonColumn).OrderBy(x => x.Name);
708708

@@ -1743,7 +1743,7 @@ protected virtual void TrackData(
17431743
AddSeedData(sourceEntityType, _sourceIdentityMaps, EntityState.Deleted);
17441744
}
17451745
}
1746-
1746+
17471747
private void AddSeedData(IEntityType entityType, Dictionary<ITable, IRowIdentityMap> identityMaps, EntityState initialState)
17481748
{
17491749
var sensitiveLoggingEnabled = CommandBatchPreparerDependencies.LoggingOptions.IsSensitiveDataLoggingEnabled;
@@ -1816,21 +1816,21 @@ private void AddSeedData(IEntityType entityType, Dictionary<ITable, IRowIdentity
18161816
BuildValuesString(key),
18171817
table.SchemaQualifiedName));
18181818
}
1819-
1819+
18201820
throw new InvalidOperationException(
18211821
RelationalStrings.DuplicateSeedData(
18221822
entityType.DisplayName(),
18231823
table.SchemaQualifiedName));
18241824
}
1825-
1825+
18261826
command = existingCommand;
18271827
}
18281828
else
18291829
{
18301830
command = CommandBatchPreparerDependencies.ModificationCommandFactory.CreateNonTrackedModificationCommand(
18311831
new NonTrackedModificationCommandParameters(table, sensitiveLoggingEnabled));
18321832
command.EntityState = initialState;
1833-
1833+
18341834
identityMap.Add(key, command);
18351835
}
18361836

@@ -1914,7 +1914,7 @@ private void AddSeedData(IEntityType entityType, Dictionary<ITable, IRowIdentity
19141914
writeValue = writeValue
19151915
&& initialState != EntityState.Deleted
19161916
&& property.GetBeforeSaveBehavior() == PropertySaveBehavior.Save;
1917-
1917+
19181918
command.AddColumnModification(
19191919
new ColumnModificationParameters(
19201920
column, originalValue: value, value, property, columnMapping.TypeMapping,
@@ -1982,7 +1982,7 @@ protected virtual void DiffData(
19821982
{
19831983
return;
19841984
}
1985-
1985+
19861986
var tableMapping = new Dictionary<ITable, (ITable, IRowIdentityMap)?>();
19871987
var unchangedColumns = new List<IColumnModification>();
19881988
var overridenColumns = new List<IColumnModification>();
@@ -2049,10 +2049,10 @@ protected virtual void DiffData(
20492049
{
20502050
targetRow.EntityState = EntityState.Unchanged;
20512051
}
2052-
2052+
20532053
continue;
20542054
}
2055-
2055+
20562056
if (sourceTable.IsExcludedFromMigrations
20572057
|| targetTable.IsExcludedFromMigrations)
20582058
{
@@ -2162,7 +2162,7 @@ protected virtual IEnumerable<MigrationOperation> GetDataOperations(
21622162
DiffContext diffContext)
21632163
{
21642164
TrackData(source, target, diffContext);
2165-
2165+
21662166
DiffData(source, target, diffContext);
21672167

21682168
var dataOperations = GetDataOperations(forSource: true, diffContext)

0 commit comments

Comments
 (0)