diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index f64fce248fa..e33d78bc4f8 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -2144,16 +2144,19 @@ protected virtual void ValidateMappingFragments( entityType.DisplayName(), fragment.StoreObject.DisplayName())); } - var unmatchedLeafRowInternalFk = entityType.FindRowInternalForeignKeys(fragment.StoreObject) - .FirstOrDefault( - fk => entityType.FindRowInternalForeignKeys(mainStoreObject.Value) - .All(mainFk => mainFk.PrincipalEntityType != fk.PrincipalEntityType)); - if (unmatchedLeafRowInternalFk != null) + foreach (var foreignKey in entityType.FindRowInternalForeignKeys(fragment.StoreObject)) { - throw new InvalidOperationException( - RelationalStrings.EntitySplittingUnmatchedMainTableSplitting( - entityType.DisplayName(), fragment.StoreObject.DisplayName(), - unmatchedLeafRowInternalFk.PrincipalEntityType.DisplayName())); + var principalMainFragment = StoreObjectIdentifier.Create( + foreignKey.PrincipalEntityType, fragment.StoreObject.StoreObjectType)!.Value; + if (principalMainFragment != mainStoreObject) + { + throw new InvalidOperationException( + RelationalStrings.EntitySplittingUnmatchedMainTableSplitting( + entityType.DisplayName(), + fragment.StoreObject.DisplayName(), + foreignKey.PrincipalEntityType.DisplayName(), + principalMainFragment.DisplayName())); + } } var propertiesFound = false; diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 4a2a22d0240..85f252e0204 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -662,12 +662,12 @@ public static string EntitySplittingUnmappedMainFragment(object? entityType, obj entityType, storeObject, storeObjectType); /// - /// Entity type '{entityType}' has a split mapping for '{storeObject}' that shares the table with '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to a table that '{principalEntityType}' is also mapped to. + /// Entity type '{entityType}' has a split mapping for '{storeObject}' that is shared with the entity type '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to '{principalStoreObject}'. /// - public static string EntitySplittingUnmatchedMainTableSplitting(object? entityType, object? storeObject, object? principalEntityType) + public static string EntitySplittingUnmatchedMainTableSplitting(object? entityType, object? storeObject, object? principalEntityType, object? principalStoreObject) => string.Format( - GetString("EntitySplittingUnmatchedMainTableSplitting", nameof(entityType), nameof(storeObject), nameof(principalEntityType)), - entityType, storeObject, principalEntityType); + GetString("EntitySplittingUnmatchedMainTableSplitting", nameof(entityType), nameof(storeObject), nameof(principalEntityType), nameof(principalStoreObject)), + entityType, storeObject, principalEntityType, principalStoreObject); /// /// An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information. diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index e883a10d1ef..fcac0a5f7f9 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -362,7 +362,7 @@ Entity type '{entityType}' has a split mapping for '{storeObject}', but it doesn't have a main mapping of the same type. Map '{entityType}' to '{storeObjectType}'. - Entity type '{entityType}' has a split mapping for '{storeObject}' that shares the table with '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to a table that '{principalEntityType}' is also mapped to. + Entity type '{entityType}' has a split mapping for '{storeObject}' that is shared with the entity type '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to '{principalStoreObject}'. An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information. diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index 313e9c11ba5..47b3cdd560b 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -800,7 +800,45 @@ public void Detects_entity_splitting_with_partial_table_splitting() }); VerifyError( - RelationalStrings.EntitySplittingUnmatchedMainTableSplitting(nameof(OrderDetails), "Order", nameof(Order)), + RelationalStrings.EntitySplittingUnmatchedMainTableSplitting(nameof(OrderDetails), "Order", nameof(Order), "Order"), + modelBuilder); + } + + [ConditionalFact] + public void Detects_entity_splitting_with_reverse_table_splitting() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity( + cb => + { + cb.Ignore(c => c.Customer); + + cb.ToTable("Order"); + + cb.SplitToTable( + "OrderDetails", tb => + { + tb.Property(c => c.PartitionId); + }); + + cb.OwnsOne( + c => c.OrderDetails, db => + { + db.ToTable("OrderDetails"); + + db.Property("OtherAddress"); + db.SplitToTable( + "Order", tb => + { + tb.Property("OtherAddress"); + }); + }); + cb.Navigation(c => c.OrderDetails).IsRequired(); + }); + + VerifyError( + RelationalStrings.EntitySplittingUnmatchedMainTableSplitting(nameof(OrderDetails), "Order", nameof(Order), "Order"), modelBuilder); }