Skip to content

Commit

Permalink
Query: Wrap up support for owned/weak entities
Browse files Browse the repository at this point in the history
Close #15285

InMemory tracked by #16994
  • Loading branch information
smitpatel committed Aug 6, 2019
1 parent b4c4a1a commit 2bf6e04
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,23 @@ protected override Expression VisitExtension(Expression extensionExpression)
}

case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression:
return materializeCollectionNavigationExpression.Navigation.IsEmbedded()
? base.Visit(materializeCollectionNavigationExpression.Subquery)
: base.VisitExtension(materializeCollectionNavigationExpression);
if (materializeCollectionNavigationExpression.Navigation.IsEmbedded())
{
var subquery = materializeCollectionNavigationExpression.Subquery;
// Unwrap AsQueryable around the subquery if present
if (subquery is MethodCallExpression innerMethodCall
&& innerMethodCall.Method.IsGenericMethod
&& innerMethodCall.Method.GetGenericMethodDefinition() == QueryableMethodProvider.AsQueryableMethodInfo)
{
subquery = innerMethodCall.Arguments[0];
}

return base.Visit(subquery);
}
else
{
return base.VisitExtension(materializeCollectionNavigationExpression);
}

case IncludeExpression includeExpression:
return _clientEval ? base.VisitExtension(includeExpression) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal
{
public class RelationalProjectionBindingExpressionVisitor : ExpressionVisitor
{
private readonly QueryableMethodTranslatingExpressionVisitor _queryableMethodTranslatingExpressionVisitor;
private readonly RelationalQueryableMethodTranslatingExpressionVisitor _queryableMethodTranslatingExpressionVisitor;
private readonly RelationalSqlTranslatingExpressionVisitor _sqlTranslator;

private SelectExpression _selectExpression;
Expand All @@ -23,7 +23,7 @@ private readonly IDictionary<ProjectionMember, Expression> _projectionMapping
private readonly Stack<ProjectionMember> _projectionMembers = new Stack<ProjectionMember>();

public RelationalProjectionBindingExpressionVisitor(
QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor,
RelationalQueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor,
RelationalSqlTranslatingExpressionVisitor sqlTranslatingExpressionVisitor)
{
_queryableMethodTranslatingExpressionVisitor = queryableMethodTranslatingExpressionVisitor;
Expand All @@ -37,13 +37,15 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio

_projectionMembers.Push(new ProjectionMember());

var result = Visit(expression);
var expandedExpression = _queryableMethodTranslatingExpressionVisitor.ExpandWeakEntities(_selectExpression, expression);
var result = Visit(expandedExpression);

if (result == null)
{
_clientEval = true;

result = Visit(expression);
expandedExpression = _queryableMethodTranslatingExpressionVisitor.ExpandWeakEntities(_selectExpression, expression);
result = Visit(expandedExpression);

_projectionMapping.Clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ protected override ShapedQueryExpression TranslateAverage(ShapedQueryExpression
var newSelector = selector == null
|| selector.Body == selector.Parameters[0]
? selectExpression.GetMappedProjection(new ProjectionMember())
: ReplacingExpressionVisitor.Replace(selector.Parameters.Single(), source.ShaperExpression, selector.Body);
: RemapLambdaBody(source, selector);

var projection = _sqlTranslator.TranslateAverage(newSelector);

Expand Down Expand Up @@ -326,6 +326,8 @@ protected override ShapedQueryExpression TranslateGroupBy(
{ original2, source.ShaperExpression }
}).Visit(resultSelector.Body);

newResultSelectorBody = ExpandWeakEntities(selectExpression, newResultSelectorBody);

source.ShaperExpression = _projectionBindingExpressionVisitor.Translate(selectExpression, newResultSelectorBody);

return source;
Expand Down Expand Up @@ -590,7 +592,7 @@ protected override ShapedQueryExpression TranslateMax(ShapedQueryExpression sour
var newSelector = selector == null
|| selector.Body == selector.Parameters[0]
? selectExpression.GetMappedProjection(new ProjectionMember())
: ReplacingExpressionVisitor.Replace(selector.Parameters.Single(), source.ShaperExpression, selector.Body);
: RemapLambdaBody(source, selector);

var projection = _sqlTranslator.TranslateMax(newSelector);

Expand All @@ -605,7 +607,7 @@ protected override ShapedQueryExpression TranslateMin(ShapedQueryExpression sour
var newSelector = selector == null
|| selector.Body == selector.Parameters[0]
? selectExpression.GetMappedProjection(new ProjectionMember())
: ReplacingExpressionVisitor.Replace(selector.Parameters.Single(), source.ShaperExpression, selector.Body);
: RemapLambdaBody(source, selector);

var projection = _sqlTranslator.TranslateMin(newSelector);

Expand Down Expand Up @@ -701,7 +703,8 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
selectExpression.PushdownIntoSubquery();
}

var newSelectorBody = RemapLambdaBody(source, selector);
var newSelectorBody = ReplacingExpressionVisitor.Replace(
selector.Parameters.Single(), source.ShaperExpression, selector.Body);
source.ShaperExpression = _projectionBindingExpressionVisitor.Translate(selectExpression, newSelectorBody);

return source;
Expand Down Expand Up @@ -825,7 +828,7 @@ protected override ShapedQueryExpression TranslateSum(ShapedQueryExpression sour
var newSelector = selector == null
|| selector.Body == selector.Parameters[0]
? selectExpression.GetMappedProjection(new ProjectionMember())
: ReplacingExpressionVisitor.Replace(selector.Parameters.Single(), source.ShaperExpression, selector.Body);
: RemapLambdaBody(source, selector);

var projection = _sqlTranslator.TranslateSum(newSelector);

Expand Down Expand Up @@ -901,10 +904,12 @@ private Expression RemapLambdaBody(ShapedQueryExpression shapedQueryExpression,
var lambdaBody = ReplacingExpressionVisitor.Replace(
lambdaExpression.Parameters.Single(), shapedQueryExpression.ShaperExpression, lambdaExpression.Body);

var selectExpression = (SelectExpression)shapedQueryExpression.QueryExpression;
lambdaBody = _weakEntityExpandingExpressionVisitor.Expand(selectExpression, lambdaBody);
return ExpandWeakEntities((SelectExpression)shapedQueryExpression.QueryExpression, lambdaBody);
}

return lambdaBody;
internal Expression ExpandWeakEntities(SelectExpression selectExpression, Expression lambdaBody)
{
return _weakEntityExpandingExpressionVisitor.Expand(selectExpression, lambdaBody);
}

public class WeakEntityExpandingExpressionVisitor : ExpressionVisitor
Expand All @@ -914,7 +919,8 @@ public class WeakEntityExpandingExpressionVisitor : ExpressionVisitor
private readonly ISqlExpressionFactory _sqlExpressionFactory;

public WeakEntityExpandingExpressionVisitor(
RelationalSqlTranslatingExpressionVisitor sqlTranslator, ISqlExpressionFactory sqlExpressionFactory)
RelationalSqlTranslatingExpressionVisitor sqlTranslator,
ISqlExpressionFactory sqlExpressionFactory)
{
_sqlTranslator = sqlTranslator;
_sqlExpressionFactory = sqlExpressionFactory;
Expand Down Expand Up @@ -965,6 +971,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp

return methodCallExpression.Update(null, new[] { source, methodCallExpression.Arguments[1] });
}

return base.VisitMethodCall(methodCallExpression);
}

Expand All @@ -986,86 +993,110 @@ private Expression Expand(Expression source, MemberIdentity member)
}
}

if (source is EntityShaperExpression entityShaperExpression)
if (!(source is EntityShaperExpression entityShaperExpression))
{
var entityType = entityShaperExpression.EntityType;
if (convertedType != null)
{
entityType = entityType.RootType().GetDerivedTypesInclusive()
.FirstOrDefault(et => et.ClrType == convertedType);

if (entityType == null)
{
return null;
}
}
return null;
}

var navigation = member.MemberInfo != null
? entityType.FindNavigation(member.MemberInfo)
: entityType.FindNavigation(member.Name);
var entityType = entityShaperExpression.EntityType;
if (convertedType != null)
{
entityType = entityType.RootType().GetDerivedTypesInclusive()
.FirstOrDefault(et => et.ClrType == convertedType);

if (navigation != null
&& navigation.ForeignKey.IsOwnership)
if (entityType == null)
{
if (navigation.IsCollection())
{
var targetEntityType = navigation.GetTargetType();
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
.Concat(navigation.ForeignKey.Properties)
.Select(p => p.ClrType)
.Any(t => t.IsNullableType());
return null;
}
}

var outerKey = CreateKeyAccessExpression(
entityShaperExpression, navigation.ForeignKey.PrincipalKey.Properties, makeNullable);
var innerKey = CreateKeyAccessExpression(
innerShapedQuery.ShaperExpression, navigation.ForeignKey.Properties, makeNullable);
var navigation = member.MemberInfo != null
? entityType.FindNavigation(member.MemberInfo)
: entityType.FindNavigation(member.Name);

var correlationPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));
if (navigation == null)
{
return null;
}

innerSelectExpression.ApplyPredicate(correlationPredicate);
var targetEntityType = navigation.GetTargetType();
if (targetEntityType == null
|| (!targetEntityType.HasDefiningNavigation()
&& !targetEntityType.IsOwned()))
{
return null;
}

return innerShapedQuery;
}

var entityProjectionExpression = (EntityProjectionExpression)
(entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression
? _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember)
: entityShaperExpression.ValueBufferExpression);
if (navigation.IsCollection())
{
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
.Concat(navigation.ForeignKey.Properties)
.Select(p => p.ClrType)
.Any(t => t.IsNullableType());

var outerKey = CreateKeyAccessExpression(
entityShaperExpression,
navigation.IsDependentToPrincipal()
? navigation.ForeignKey.Properties
: navigation.ForeignKey.PrincipalKey.Properties,
makeNullable);
var innerKey = CreateKeyAccessExpression(
innerShapedQuery.ShaperExpression,
navigation.IsDependentToPrincipal()
? navigation.ForeignKey.PrincipalKey.Properties
: navigation.ForeignKey.Properties,
makeNullable);

var correlationPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));

innerSelectExpression.ApplyPredicate(correlationPredicate);

return innerShapedQuery;
}

var innerShaper = entityProjectionExpression.BindNavigation(navigation);
if (innerShaper == null)
{
var targetEntityType = navigation.GetTargetType();
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
.Concat(navigation.ForeignKey.Properties)
.Select(p => p.ClrType)
.Any(t => t.IsNullableType());

var outerKey = CreateKeyAccessExpression(
entityShaperExpression, navigation.ForeignKey.PrincipalKey.Properties, makeNullable);
var innerKey = CreateKeyAccessExpression(
innerShapedQuery.ShaperExpression, navigation.ForeignKey.Properties, makeNullable);

var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));
_selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate, null);
var leftJoinTable = ((LeftJoinExpression)_selectExpression.Tables.Last()).Table;
innerShaper = new EntityShaperExpression(targetEntityType,
new EntityProjectionExpression(targetEntityType, leftJoinTable, true),
true);
entityProjectionExpression.AddNavigationBinding(navigation, innerShaper);
}
var entityProjectionExpression = (EntityProjectionExpression)
(entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression
? _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember)
: entityShaperExpression.ValueBufferExpression);

return innerShaper;
}
var innerShaper = entityProjectionExpression.BindNavigation(navigation);
if (innerShaper == null)
{
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
.Concat(navigation.ForeignKey.Properties)
.Select(p => p.ClrType)
.Any(t => t.IsNullableType());

var outerKey = CreateKeyAccessExpression(
entityShaperExpression,
navigation.IsDependentToPrincipal()
? navigation.ForeignKey.Properties
: navigation.ForeignKey.PrincipalKey.Properties,
makeNullable);
var innerKey = CreateKeyAccessExpression(
innerShapedQuery.ShaperExpression,
navigation.IsDependentToPrincipal()
? navigation.ForeignKey.PrincipalKey.Properties
: navigation.ForeignKey.Properties,
makeNullable);

var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));
_selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate, null);
var leftJoinTable = ((LeftJoinExpression)_selectExpression.Tables.Last()).Table;
innerShaper = new EntityShaperExpression(targetEntityType,
new EntityProjectionExpression(targetEntityType, leftJoinTable, true),
true);
entityProjectionExpression.AddNavigationBinding(navigation, innerShaper);
}

return null;
return innerShaper;
}

public static Expression CreateKeyAccessExpression(
Expand Down
Loading

0 comments on commit 2bf6e04

Please sign in to comment.