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

Query: Wrap up support for owned/weak entities #16995

Merged
merged 1 commit into from
Aug 6, 2019
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
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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't figure out better way to do this for now.
The issue is, projectionBinding may revisit expression multiple times but ShapedQueryExpression is mutating so we have mutated on failed translation and we cannot reuse it. Hence we need to expand weak entities (introducing ShapedQueryExpression) from projection binding expression visitor.

{
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