Skip to content

Commit bb72359

Browse files
committed
Query: Full support for entity splitting
Resolves #620
1 parent 3a9dd22 commit bb72359

15 files changed

+1057
-508
lines changed

src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs

+20-125
Original file line numberDiff line numberDiff line change
@@ -1693,13 +1693,24 @@ protected override Expression VisitExtension(Expression extensionExpression)
16931693
{
16941694
if (navigation.IsCollection)
16951695
{
1696-
var innerSelectExpression = BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
1697-
entityProjectionExpression,
1698-
targetEntityType.GetViewOrTableMappings().Single().Table,
1699-
navigation);
1696+
// just need any column - we use it only to extract the table it originated from
1697+
var sourceColumn = entityProjectionExpression
1698+
.BindProperty(
1699+
navigation.IsOnDependent
1700+
? foreignKey.Properties[0]
1701+
: foreignKey.PrincipalKey.Properties[0]);
1702+
1703+
var sourceTable = FindRootTableExpressionForColumn(sourceColumn);
1704+
TableExpressionBase ownedTable = new TableExpression(targetEntityType.GetViewOrTableMappings().Single().Table);
1705+
1706+
foreach (var annotation in sourceTable.GetAnnotations())
1707+
{
1708+
ownedTable = ownedTable.AddAnnotation(annotation.Name, annotation.Value);
1709+
}
17001710

1701-
var innerShapedQuery = CreateShapedQueryExpression(
1702-
targetEntityType, innerSelectExpression);
1711+
var innerSelectExpression = new SelectExpression(targetEntityType, ownedTable);
1712+
1713+
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);
17031714

17041715
var makeNullable = foreignKey.PrincipalKey.Properties
17051716
.Concat(foreignKey.Properties)
@@ -1746,102 +1757,9 @@ outerKey is NewArrayExpression newArrayExpression
17461757
Expression.Quote(correlationPredicate));
17471758
}
17481759

1749-
var innerShaper = entityProjectionExpression.BindNavigation(navigation);
1750-
if (innerShaper == null)
1751-
{
1752-
// Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630
1753-
// So there is no handling for dependent having TPT/TPC
1754-
// If navigation is defined on derived type and entity type is part of TPT then we need to get ITableBase for derived type.
1755-
// TODO: The following code should also handle Function and SqlQuery mappings
1756-
var table = navigation.DeclaringEntityType.BaseType == null
1757-
|| entityType.FindDiscriminatorProperty() != null
1758-
? navigation.DeclaringEntityType.GetViewOrTableMappings().Single().Table
1759-
: navigation.DeclaringEntityType.GetViewOrTableMappings().Select(tm => tm.Table)
1760-
.Except(navigation.DeclaringEntityType.BaseType.GetViewOrTableMappings().Select(tm => tm.Table))
1761-
.Single();
1762-
if (table.GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType).Contains(foreignKey))
1763-
{
1764-
// Mapped to same table
1765-
// We get identifying column to figure out tableExpression to pull columns from and nullability of most principal side
1766-
var identifyingColumn = entityProjectionExpression.BindProperty(entityType.FindPrimaryKey()!.Properties.First());
1767-
var principalNullable = identifyingColumn.IsNullable
1768-
// Also make nullable if navigation is on derived type and and principal is TPT
1769-
// Since identifying PK would be non-nullable but principal can still be null
1770-
// Derived owned navigation does not de-dupe the PK column which for principal is from base table
1771-
// and for dependent on derived table
1772-
|| (entityType.FindDiscriminatorProperty() == null
1773-
&& navigation.DeclaringEntityType.IsStrictlyDerivedFrom(entityShaperExpression.EntityType));
1774-
1775-
var entityProjection = _selectExpression.GenerateWeakEntityProjectionExpression(
1776-
targetEntityType, table, identifyingColumn.Name, identifyingColumn.Table, principalNullable);
1777-
1778-
if (entityProjection != null)
1779-
{
1780-
innerShaper = new RelationalEntityShaperExpression(targetEntityType, entityProjection, principalNullable);
1781-
}
1782-
}
1783-
1784-
if (innerShaper == null)
1785-
{
1786-
// InnerShaper is still null if either it is not table sharing or we failed to find table to pick data from
1787-
// So we find the table it is mapped to and generate join with it.
1788-
// Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630
1789-
// So there is no handling for dependent having TPT
1790-
table = targetEntityType.GetViewOrTableMappings().Single().Table;
1791-
var innerSelectExpression = BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
1792-
entityProjectionExpression,
1793-
table,
1794-
navigation);
1795-
1796-
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);
1797-
1798-
var makeNullable = foreignKey.PrincipalKey.Properties
1799-
.Concat(foreignKey.Properties)
1800-
.Select(p => p.ClrType)
1801-
.Any(t => t.IsNullableType());
1802-
1803-
var outerKey = entityShaperExpression.CreateKeyValuesExpression(
1804-
navigation.IsOnDependent
1805-
? foreignKey.Properties
1806-
: foreignKey.PrincipalKey.Properties,
1807-
makeNullable);
1808-
var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression(
1809-
navigation.IsOnDependent
1810-
? foreignKey.PrincipalKey.Properties
1811-
: foreignKey.Properties,
1812-
makeNullable);
1813-
1814-
var joinPredicate = _sqlTranslator.Translate(
1815-
Infrastructure.ExpressionExtensions.CreateEqualsExpression(outerKey, innerKey))!;
1816-
// Following conditions should match conditions for pushdown on outer during SelectExpression.AddJoin method
1817-
var pushdownRequired = _selectExpression.Limit != null
1818-
|| _selectExpression.Offset != null
1819-
|| _selectExpression.IsDistinct
1820-
|| _selectExpression.GroupBy.Count > 0;
1821-
_selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate);
1822-
1823-
// If pushdown was required on SelectExpression then we need to fetch the updated entity projection
1824-
if (pushdownRequired)
1825-
{
1826-
if (doee is not null)
1827-
{
1828-
entityShaperExpression = _deferredOwnedExpansionRemover.UnwrapDeferredEntityProjectionExpression(doee);
1829-
}
1830-
1831-
entityProjectionExpression = GetEntityProjectionExpression(entityShaperExpression);
1832-
}
1833-
1834-
var leftJoinTable = _selectExpression.Tables.Last();
1835-
1836-
innerShaper = new RelationalEntityShaperExpression(
1837-
targetEntityType,
1838-
_selectExpression.GenerateWeakEntityProjectionExpression(
1839-
targetEntityType, table, null, leftJoinTable, nullable: true)!,
1840-
nullable: true);
1841-
}
1842-
1843-
entityProjectionExpression.AddNavigationBinding(navigation, innerShaper);
1844-
}
1760+
var innerShaper = entityProjectionExpression.BindNavigation(navigation)
1761+
?? _selectExpression.GenerateOwnedReferenceEntityProjectionExpression(
1762+
entityProjectionExpression, navigation, _sqlExpressionFactory);
18451763
}
18461764

18471765
return doee is not null
@@ -1851,29 +1769,6 @@ outerKey is NewArrayExpression newArrayExpression
18511769
(ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression,
18521770
navigation);
18531771

1854-
SelectExpression BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable(
1855-
EntityProjectionExpression entityProjectionExpression,
1856-
ITableBase targetTable,
1857-
INavigation navigation)
1858-
{
1859-
// just need any column - we use it only to extract the table it originated from
1860-
var sourceColumn = entityProjectionExpression
1861-
.BindProperty(
1862-
navigation.IsOnDependent
1863-
? foreignKey.Properties[0]
1864-
: foreignKey.PrincipalKey.Properties[0]);
1865-
1866-
var sourceTable = FindRootTableExpressionForColumn(sourceColumn);
1867-
TableExpressionBase ownedTable = new TableExpression(targetTable);
1868-
1869-
foreach (var annotation in sourceTable.GetAnnotations())
1870-
{
1871-
ownedTable = ownedTable.AddAnnotation(annotation.Name, annotation.Value);
1872-
}
1873-
1874-
return _sqlExpressionFactory.Select(targetEntityType, ownedTable);
1875-
}
1876-
18771772
static TableExpressionBase FindRootTableExpressionForColumn(ColumnExpression column)
18781773
{
18791774
var table = column.Table;

0 commit comments

Comments
 (0)