Skip to content
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

Validate that if any fragments use table sharing then the main fragments are mapped to the same table #29285

Merged
merged 1 commit into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2143,16 +2143,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;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
<value>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}'.</value>
</data>
<data name="EntitySplittingUnmatchedMainTableSplitting" xml:space="preserve">
<value>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.</value>
<value>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}'.</value>
</data>
<data name="ErrorMaterializingProperty" xml:space="preserve">
<value>An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,116 +904,6 @@ await AssertQuery(
entryCount: 10);
}

[ConditionalTheory(Skip = "Issue#29104")]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Split_entity_owning_a_split_reference_with_table_sharing_2(bool async)
{
await InitializeContextFactoryAsync(
mb =>
{
mb.Entity<EntityOne>(
b =>
{
b.ToTable("SplitEntityOnePart1");

b.SplitToTable("SplitEntityOnePart2",
tb =>
{
tb.Property(e => e.IntValue3);
tb.Property(e => e.StringValue3);
});

b.SplitToTable("SplitEntityOnePart3",
tb =>
{
tb.Property(e => e.IntValue4);
tb.Property(e => e.StringValue4);
});

b.OwnsOne(e => e.OwnedReference,
o =>
{
o.ToTable("SplitEntityOnePart2");

o.SplitToTable("SplitEntityOnePart3",
t =>
{
t.Property(e => e.OwnedIntValue3);
t.Property(e => e.OwnedStringValue3);
});

o.SplitToTable("SplitEntityOnePart1",
t =>
{
t.Property(e => e.OwnedIntValue4);
t.Property(e => e.OwnedStringValue4);
});
});
});
});

await AssertQuery(
async,
ss => ss.Set<EntityOne>(),
elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude<EntityOne>(i => i.OwnedReference)),
entryCount: 10);
}

[ConditionalTheory(Skip = "Issue#29104")]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Split_entity_owning_a_split_reference_with_table_sharing_3(bool async)
{
await InitializeContextFactoryAsync(
mb =>
{
mb.Entity<EntityOne>(
b =>
{
b.ToTable("SplitEntityOnePart1");

b.SplitToTable("SplitEntityOnePart2",
tb =>
{
tb.Property(e => e.IntValue3);
tb.Property(e => e.StringValue3);
});

b.SplitToTable("SplitEntityOnePart3",
tb =>
{
tb.Property(e => e.IntValue4);
tb.Property(e => e.StringValue4);
});

b.OwnsOne(e => e.OwnedReference,
o =>
{
o.ToTable("SplitEntityOnePart3");

o.SplitToTable("SplitEntityOnePart2",
t =>
{
t.Property(e => e.OwnedIntValue3);
t.Property(e => e.OwnedStringValue3);
});

o.SplitToTable("SplitEntityOnePart1",
t =>
{
t.Property(e => e.OwnedIntValue4);
t.Property(e => e.OwnedStringValue4);
});
});
});
});

await AssertQuery(
async,
ss => ss.Set<EntityOne>(),
elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude<EntityOne>(i => i.OwnedReference)),
entryCount: 10);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Split_entity_owning_a_split_reference_with_table_sharing_4(bool async)
Expand Down Expand Up @@ -1069,61 +959,6 @@ await AssertQuery(
entryCount: 10);
}

[ConditionalTheory(Skip = "Issue#29104")]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Split_entity_owning_a_split_reference_with_table_sharing_5(bool async)
{
await InitializeContextFactoryAsync(
mb =>
{
mb.Entity<EntityOne>(
b =>
{
b.ToTable("SplitEntityOnePart1");

b.SplitToTable("SplitEntityOnePart2",
tb =>
{
tb.Property(e => e.IntValue3);
tb.Property(e => e.StringValue3);
});

b.SplitToTable("SplitEntityOnePart3",
tb =>
{
tb.Property(e => e.IntValue4);
tb.Property(e => e.StringValue4);
});

b.OwnsOne(e => e.OwnedReference,
o =>
{
o.ToTable("SplitEntityOnePart2");

o.SplitToTable("SplitEntityOnePart1",
t =>
{
t.Property(e => e.OwnedIntValue3);
t.Property(e => e.OwnedStringValue3);
});

o.SplitToTable("OwnedReferencePart3",
t =>
{
t.Property(e => e.OwnedIntValue4);
t.Property(e => e.OwnedStringValue4);
});
});
});
});

await AssertQuery(
async,
ss => ss.Set<EntityOne>(),
elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude<EntityOne>(i => i.OwnedReference)),
entryCount: 10);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Split_entity_owning_a_split_reference_with_table_sharing_6(bool async)
Expand Down Expand Up @@ -1155,7 +990,7 @@ await InitializeContextFactoryAsync(
{
o.ToTable("SplitEntityOnePart2");

o.SplitToTable("SplitEntityOnePart3",
o.SplitToTable("OwnedReferencePart2",
t =>
{
t.Property(e => e.OwnedIntValue3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,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<Order>(
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<string>("OtherAddress");
db.SplitToTable(
"Order", tb =>
{
tb.Property("OtherAddress");
});
});
cb.Navigation(c => c.OrderDetails).IsRequired();
});

VerifyError(
RelationalStrings.EntitySplittingUnmatchedMainTableSplitting(nameof(OrderDetails), "Order", nameof(Order), "Order"),
modelBuilder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,20 +279,6 @@ FROM [SplitEntityOnePart1] AS [s]
INNER JOIN [SplitEntityOnePart2] AS [s1] ON [s].[Id] = [s1].[Id]");
}

public override async Task Split_entity_owning_a_split_reference_with_table_sharing_2(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_2(async);

AssertSql();
}

public override async Task Split_entity_owning_a_split_reference_with_table_sharing_3(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_3(async);

AssertSql();
}

public override async Task Split_entity_owning_a_split_reference_with_table_sharing_4(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_4(async);
Expand All @@ -305,19 +291,17 @@ FROM [SplitEntityOnePart1] AS [s]
LEFT JOIN [OwnedReferencePart3] AS [o] ON [s].[Id] = [o].[EntityOneId]");
}

public override async Task Split_entity_owning_a_split_reference_with_table_sharing_5(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_5(async);

AssertSql();
}

[ConditionalTheory(Skip = "Issue29075")]
public override async Task Split_entity_owning_a_split_reference_with_table_sharing_6(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_6(async);

AssertSql();
AssertSql(
@"SELECT [s].[Id], [s].[EntityThreeId], [s].[IntValue1], [s].[IntValue2], [s1].[IntValue3], [s0].[IntValue4], [s].[StringValue1], [s].[StringValue2], [s1].[StringValue3], [s0].[StringValue4], [s1].[Id], [s1].[OwnedReference_Id], [s1].[OwnedReference_OwnedIntValue1], [s1].[OwnedReference_OwnedIntValue2], [o0].[OwnedIntValue3], [o].[OwnedIntValue4], [s1].[OwnedReference_OwnedStringValue1], [s1].[OwnedReference_OwnedStringValue2], [o0].[OwnedStringValue3], [o].[OwnedStringValue4]
FROM [SplitEntityOnePart1] AS [s]
INNER JOIN [SplitEntityOnePart3] AS [s0] ON [s].[Id] = [s0].[Id]
INNER JOIN [SplitEntityOnePart2] AS [s1] ON [s].[Id] = [s1].[Id]
LEFT JOIN [OwnedReferencePart3] AS [o] ON [s1].[Id] = [o].[EntityOneId]
LEFT JOIN [OwnedReferencePart2] AS [o0] ON [s1].[Id] = [o0].[EntityOneId]");
}

public override async Task Tph_entity_owning_a_split_reference_on_base_with_table_sharing(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,18 @@ LEFT JOIN (
) AS ""t"" ON ""e"".""Id"" = ""t"".""EntityOneId""
ORDER BY ""e"".""Id"", ""t"".""EntityOneId""");
}

public override async Task Split_entity_owning_a_split_reference_with_table_sharing_6(bool async)
{
await base.Split_entity_owning_a_split_reference_with_table_sharing_6(async);

AssertSql(
@"SELECT ""s"".""Id"", ""s"".""EntityThreeId"", ""s"".""IntValue1"", ""s"".""IntValue2"", ""s1"".""IntValue3"", ""s0"".""IntValue4"", ""s"".""StringValue1"", ""s"".""StringValue2"", ""s1"".""StringValue3"", ""s0"".""StringValue4"", ""s1"".""Id"", ""s1"".""OwnedReference_Id"", ""s1"".""OwnedReference_OwnedIntValue1"", ""s1"".""OwnedReference_OwnedIntValue2"", ""s0"".""OwnedReference_OwnedIntValue3"", ""o"".""OwnedIntValue4"", ""s1"".""OwnedReference_OwnedStringValue1"", ""s1"".""OwnedReference_OwnedStringValue2"", ""s0"".""OwnedReference_OwnedStringValue3"", ""o"".""OwnedStringValue4""
@"SELECT ""s"".""Id"", ""s"".""EntityThreeId"", ""s"".""IntValue1"", ""s"".""IntValue2"", ""s1"".""IntValue3"", ""s0"".""IntValue4"", ""s"".""StringValue1"", ""s"".""StringValue2"", ""s1"".""StringValue3"", ""s0"".""StringValue4"", ""s1"".""Id"", ""s1"".""OwnedReference_Id"", ""s1"".""OwnedReference_OwnedIntValue1"", ""s1"".""OwnedReference_OwnedIntValue2"", ""o0"".""OwnedIntValue3"", ""o"".""OwnedIntValue4"", ""s1"".""OwnedReference_OwnedStringValue1"", ""s1"".""OwnedReference_OwnedStringValue2"", ""o0"".""OwnedStringValue3"", ""o"".""OwnedStringValue4""
FROM ""SplitEntityOnePart1"" AS ""s""
INNER JOIN ""SplitEntityOnePart3"" AS ""s0"" ON ""s"".""Id"" = ""s0"".""Id""
INNER JOIN ""SplitEntityOnePart2"" AS ""s1"" ON ""s"".""Id"" = ""s1"".""Id""
LEFT JOIN ""OwnedReferencePart3"" AS ""o"" ON ""s1"".""Id"" = ""o"".""EntityOneId""");
LEFT JOIN ""OwnedReferencePart3"" AS ""o"" ON ""s1"".""Id"" = ""o"".""EntityOneId""
LEFT JOIN ""OwnedReferencePart2"" AS ""o0"" ON ""s1"".""Id"" = ""o0"".""EntityOneId""");
}

public override async Task Tph_entity_owning_a_split_reference_on_base_without_table_sharing(bool async)
Expand Down