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

Refactor RelationalProjectionExpressionVisitor #7767

Closed
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 @@ -50,66 +50,98 @@ public RelationalProjectionExpressionVisitor(
/// <summary>
/// Visit a method call expression.
/// </summary>
/// <param name="node"> The expression to visit. </param>
/// <param name="methodCallExpression"> The expression to visit. </param>
/// <returns>
/// An Expression corresponding to the translated method call.
/// </returns>
protected override Expression VisitMethodCall(MethodCallExpression node)
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
Check.NotNull(node, nameof(node));
Check.NotNull(methodCallExpression, nameof(methodCallExpression));

var sqlExpression = TranslateExpression(methodCallExpression);

var objectExpression
= EntityQueryModelVisitor.IsPropertyMethod(methodCallExpression.Method)
? methodCallExpression.Arguments[0].RemoveConvert()
: methodCallExpression.Object;

var handledExpression
= TryHandleMemberOrMethodCallExpression(
methodCallExpression,
objectExpression,
sqlExpression);

if (EntityQueryModelVisitor.IsPropertyMethod(node.Method))
if (handledExpression != null)
{
var newArg0 = Visit(node.Arguments[0]);
return handledExpression;
}

if (newArg0 != node.Arguments[0])
{
return Expression.Call(
node.Method,
newArg0,
node.Arguments[1]);
}
QueryModelVisitor.RequiresClientProjection = true;
return methodCallExpression;
}

/// <summary>
/// Visit a member expression.
/// </summary>
/// <param name="memberExpression"> The expression to visit. </param>
/// <returns>
/// An Expression corresponding to the translated member.
/// </returns>
protected override Expression VisitMember(MemberExpression memberExpression)
{
Check.NotNull(memberExpression, nameof(memberExpression));

var sqlExpression = TranslateExpression(memberExpression);

return node;
var handledExpression
= TryHandleMemberOrMethodCallExpression(
memberExpression,
memberExpression.Expression.RemoveConvert(),
sqlExpression);

if (handledExpression != null)
{
return handledExpression;
}

return base.VisitMethodCall(node);
QueryModelVisitor.RequiresClientProjection = true;
return memberExpression;
}

/// <summary>
/// Visit a new expression.
/// </summary>
/// <param name="expression"> The expression to visit. </param>
/// <param name="newExpression"> The expression to visit. </param>
/// <returns>
/// An Expression corresponding to the translated new expression.
/// </returns>
protected override Expression VisitNew(NewExpression expression)
protected override Expression VisitNew(NewExpression newExpression)
{
Check.NotNull(expression, nameof(expression));
Check.NotNull(newExpression, nameof(newExpression));

var newNewExpression = base.VisitNew(expression);
var visitedNewExpression = base.VisitNew(newExpression);

var selectExpression = QueryModelVisitor.TryGetQuery(_querySource);

if (selectExpression != null)
{
for (var i = 0; i < expression.Arguments.Count; i++)
for (var i = 0; i < newExpression.Arguments.Count; i++)
{
var aliasExpression
= selectExpression.Projection
.OfType<AliasExpression>()
.SingleOrDefault(ae => ae.SourceExpression == expression.Arguments[i]);
.SingleOrDefault(ae => ae.SourceExpression == newExpression.Arguments[i]);

if (aliasExpression != null)
{
aliasExpression.SourceMember
= expression.Members?[i]
?? (expression.Arguments[i] as MemberExpression)?.Member;
= newExpression.Members?[i]
?? (newExpression.Arguments[i] as MemberExpression)?.Member;
}
}
}

return newNewExpression;
return visitedNewExpression;
}

/// <summary>
Expand All @@ -121,113 +153,148 @@ var aliasExpression
/// </returns>
public override Expression Visit(Expression node)
{
if (node == null)
{
return null;
}

if (node is ConstantExpression
|| node is NewExpression
|| node is MemberExpression
|| node is MethodCallExpression)
{
return base.Visit(node);
}

var selectExpression = QueryModelVisitor.TryGetQuery(_querySource);

if (node != null
&& !(node is ConstantExpression)
&& selectExpression != null)
if (selectExpression == null)
{
var existingProjectionsCount = selectExpression.Projection.Count;
return base.Visit(node);
}

var sqlExpression
= _sqlTranslatingExpressionVisitorFactory
.Create(QueryModelVisitor, selectExpression, inProjection: true)
.Visit(node);
var sqlExpression = TranslateExpression(node);

if (sqlExpression == null)
if (sqlExpression == null)
{
if (node is QuerySourceReferenceExpression qsre)
{
var qsre = node as QuerySourceReferenceExpression;
if (qsre == null)
{
QueryModelVisitor.RequiresClientProjection = true;
}
else
if (QueryModelVisitor.ParentQueryModelVisitor != null)
{
if (QueryModelVisitor.ParentQueryModelVisitor != null
&& selectExpression.HandlesQuerySource(qsre.ReferencedQuerySource))
{
selectExpression.ProjectStarTable = selectExpression.GetTableForQuerySource(qsre.ReferencedQuerySource);
}
selectExpression.ProjectStarTable
= selectExpression.GetTableForQuerySource(qsre.ReferencedQuerySource);
}
}
else
{
selectExpression.RemoveRangeFromProjection(existingProjectionsCount);
QueryModelVisitor.RequiresClientProjection = true;
}

if (!(node is NewExpression))
{
AliasExpression aliasExpression;
return base.Visit(node);
}

int index;
if (!(node is QuerySourceReferenceExpression))
{
if (sqlExpression is NullableExpression nullableExpression)
{
sqlExpression = nullableExpression.Operand;
}

if (!(node is QuerySourceReferenceExpression))
{
if (sqlExpression is NullableExpression nullableExpression)
{
sqlExpression = nullableExpression.Operand;
}
if (sqlExpression.TryGetColumnExpression() != null)
{
var index = selectExpression.AddToProjection(sqlExpression);

var columnExpression = sqlExpression.TryGetColumnExpression();
if (selectExpression.Projection[index] is AliasExpression aliasExpression)
{
aliasExpression.SourceExpression = node;
}

if (columnExpression != null)
{
index = selectExpression.AddToProjection(sqlExpression);
return node;
}
}

aliasExpression = selectExpression.Projection[index] as AliasExpression;
if (!(sqlExpression is ConstantExpression))
{
var targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(_querySource);

if (aliasExpression != null)
{
aliasExpression.SourceExpression = node;
}
return targetExpression.Type == typeof(ValueBuffer)
? CreateReadValueExpression(node, targetExpression, selectExpression, sqlExpression)
: node;
}

return node;
}
}
return base.Visit(node);
}

if (!(sqlExpression is ConstantExpression))
{
var targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(_querySource);
private Expression TranslateExpression(Expression expression)
=> _sqlTranslatingExpressionVisitorFactory
.Create(QueryModelVisitor, inProjection: true)
.Visit(expression);

private Expression TryHandleMemberOrMethodCallExpression(
Expression node, Expression objectExpression, Expression sqlExpression)
{
if (sqlExpression == null)
{
return null;
}

if (targetExpression.Type == typeof(ValueBuffer))
{
index = selectExpression.AddToProjection(sqlExpression);
Expression targetExpression = null;
SelectExpression targetQuery = null;

aliasExpression = selectExpression.Projection[index] as AliasExpression;
if (objectExpression is QuerySourceReferenceExpression qsre)
{
targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(qsre.ReferencedQuerySource);

if (aliasExpression != null)
{
aliasExpression.SourceExpression = node;
}
targetQuery
= QueryModelVisitor.TryGetQuery(qsre.ReferencedQuerySource);
}

var readValueExpression
= _entityMaterializerSource
.CreateReadValueCallExpression(targetExpression, index);
if (targetQuery == null)
{
targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(_querySource);

var outputDataInfo
= (node as SubQueryExpression)?.QueryModel
.GetOutputDataInfo();
targetQuery
= QueryModelVisitor.TryGetQuery(_querySource);
}

if (outputDataInfo is StreamedScalarValueInfo)
{
// Compensate for possible nulls
readValueExpression
= Expression.Coalesce(
readValueExpression,
Expression.Default(node.Type));
}
return targetQuery != null && targetExpression.Type == typeof(ValueBuffer)
? CreateReadValueExpression(node, targetExpression, targetQuery, sqlExpression)
: node;
}

return Expression.Convert(readValueExpression, node.Type);
}
private Expression CreateReadValueExpression(
Expression node,
Expression targetExpression,
SelectExpression selectExpression,
Expression sqlExpression)
{
var index = selectExpression.AddToProjection(sqlExpression);

return node;
}
}
}
if (selectExpression.Projection[index] is AliasExpression aliasExpression)
{
aliasExpression.SourceExpression = node;
}

return base.Visit(node);
if (node is SubQueryExpression subQueryExpression
&& subQueryExpression.QueryModel.GetOutputDataInfo() is StreamedScalarValueInfo)
{
// Compensate for possible nulls
return Expression.Convert(
Expression.Coalesce(
_entityMaterializerSource.CreateReadValueCallExpression(targetExpression, index),
Expression.Default(node.Type)),
node.Type);
}
else
{
return _entityMaterializerSource.CreateReadValueExpression(targetExpression, node.Type, index);
}
}
}
}
Loading