Skip to content

Commit

Permalink
Query: Avoid revisiting NewExpression in RelationalProjectionExpressi…
Browse files Browse the repository at this point in the history
…onVisitor

Do not set RequireClientProjection true for anonymous type projection
  • Loading branch information
smitpatel committed Apr 4, 2017
1 parent 67d2f2e commit 5d79f2f
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,62 +52,62 @@ 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));

if (IncludeCompiler.IsIncludeMethod(node))
if (IncludeCompiler.IsIncludeMethod(methodCallExpression))
{
return node;
return methodCallExpression;
}

if (EntityQueryModelVisitor.IsPropertyMethod(node.Method))
if (EntityQueryModelVisitor.IsPropertyMethod(methodCallExpression.Method))
{
var newArg0 = Visit(node.Arguments[0]);
var newArg0 = Visit(methodCallExpression.Arguments[0]);

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

return node;
return methodCallExpression;
}

return base.VisitMethodCall(node);
return base.VisitMethodCall(methodCallExpression);
}

/// <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 newNewExpression = 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 sourceExpression = expression.Arguments[i];
var sourceExpression = newExpression.Arguments[i];

if (_sourceExpressionProjectionMapping.ContainsKey(sourceExpression))
{
var memberInfo = expression.Members?[i]
?? (expression.Arguments[i] as MemberExpression)?.Member;
var memberInfo = newExpression.Members?[i]
?? (newExpression.Arguments[i] as MemberExpression)?.Member;

if (memberInfo != null)
{
Expand All @@ -125,103 +125,100 @@ protected override Expression VisitNew(NewExpression expression)
/// <summary>
/// Visits the given node.
/// </summary>
/// <param name="node"> The expression to visit. </param>
/// <param name="expression"> The expression to visit. </param>
/// <returns>
/// An Expression to the translated input expression.
/// </returns>
public override Expression Visit(Expression node)
public override Expression Visit(Expression expression)
{
var selectExpression = QueryModelVisitor.TryGetQuery(_querySource);

if (node != null
&& !(node is ConstantExpression)
if (expression != null
&& !(expression is ConstantExpression)
&& !(expression is NewExpression)
&& selectExpression != null)
{
var existingProjectionsCount = selectExpression.Projection.Count;

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

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

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

if (sqlExpression is ColumnExpression)
{
selectExpression.AddToProjection(sqlExpression);
if (sqlExpression is ColumnExpression)
{
selectExpression.AddToProjection(sqlExpression);

_sourceExpressionProjectionMapping[node] = sqlExpression;
_sourceExpressionProjectionMapping[expression] = sqlExpression;

return node;
}
return expression;
}
}

if (!(sqlExpression is ConstantExpression))
{
var targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(_querySource);

if (targetExpression.Type == typeof(ValueBuffer))
{
var index = selectExpression.AddToProjection(sqlExpression);
if (!(sqlExpression is ConstantExpression))
{
var targetExpression
= QueryModelVisitor.QueryCompilationContext.QuerySourceMapping
.GetExpression(_querySource);

_sourceExpressionProjectionMapping[node] = sqlExpression;
if (targetExpression.Type == typeof(ValueBuffer))
{
var index = selectExpression.AddToProjection(sqlExpression);

var readValueExpression
= _entityMaterializerSource
.CreateReadValueCallExpression(targetExpression, index);
_sourceExpressionProjectionMapping[expression] = sqlExpression;

var outputDataInfo
= (node as SubQueryExpression)?.QueryModel
.GetOutputDataInfo();
var readValueExpression
= _entityMaterializerSource
.CreateReadValueCallExpression(targetExpression, index);

if (outputDataInfo is StreamedScalarValueInfo)
{
// Compensate for possible nulls
readValueExpression
= Expression.Coalesce(
readValueExpression,
Expression.Default(node.Type));
}
var outputDataInfo
= (expression as SubQueryExpression)?.QueryModel
.GetOutputDataInfo();

return Expression.Convert(readValueExpression, node.Type);
if (outputDataInfo is StreamedScalarValueInfo)
{
// Compensate for possible nulls
readValueExpression
= Expression.Coalesce(
readValueExpression,
Expression.Default(expression.Type));
}

return node;
return Expression.Convert(readValueExpression, expression.Type);
}

return expression;
}
}
}

return base.Visit(node);
return base.Visit(expression);
}
}
}
17 changes: 15 additions & 2 deletions src/EFCore.Specification.Tests/QueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1678,12 +1678,25 @@ public virtual void Where_primitive_tracked2()
public virtual void Where_subquery_anon()
{
AssertQuery<Employee, Order>((es, os) =>
from e in es.Take(9).Select(e => new { e })
from o in os.Take(1000).Select(o => new { o })
from e in es.Take(3).Select(e => new { e })
from o in os.Take(5).Select(o => new { o })
where e.e.EmployeeID == o.o.EmployeeID
select new { e, o });
}

[ConditionalFact]
public virtual void Where_subquery_anon_nested()
{
AssertQuery<Employee, Order, Customer>(
(es, os, cs) =>
from t in (
from e in es.Take(3).Select(e => new { e }).Where(e => e.e.City == "London")
from o in os.Take(5).Select(o => new { o })
select new { e, o })
from c in cs.Take(2).Select(c => new { c })
select new { t.e, t.o, c });
}

[ConditionalFact]
public virtual void Where_subquery_expression()
{
Expand Down
1 change: 0 additions & 1 deletion src/EFCore/Query/Internal/QuerySourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;
using Remotion.Linq.Clauses;
using Remotion.Linq;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.Clauses.ResultOperators;

Expand Down
Loading

0 comments on commit 5d79f2f

Please sign in to comment.