Skip to content

Commit

Permalink
Query: Remove AnonymousObject
Browse files Browse the repository at this point in the history
- Make it implementation detail of InMemory provider which still needs it to match outer/inner keys of a join executed on server side

Resolves #20565
  • Loading branch information
smitpatel committed Apr 16, 2020
1 parent 3e1d44c commit 58a07b6
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Reflection;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Query.Internal
namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));

if (binaryExpression.Left.Type == typeof(object[])
&& binaryExpression.Left is NewArrayExpression
&& binaryExpression.NodeType == ExpressionType.Equal)
{
return Visit(ConvertObjectArrayEqualityComparison(binaryExpression));
}

var newLeft = Visit(binaryExpression.Left);
var newRight = Visit(binaryExpression.Right);

Expand Down Expand Up @@ -1098,6 +1105,38 @@ private static bool CanEvaluate(Expression expression)
}
}

private static Expression ConvertObjectArrayEqualityComparison(BinaryExpression binaryExpression)
{
var leftExpressions = ((NewArrayExpression)binaryExpression.Left).Expressions;
var rightExpressions = ((NewArrayExpression)binaryExpression.Right).Expressions;

return leftExpressions.Zip(
rightExpressions,
(l, r) =>
{
l = RemoveObjectConvert(l);
r = RemoveObjectConvert(r);
if (l.Type.IsNullableType())
{
r = r.Type.IsNullableType() ? r : Expression.Convert(r, l.Type);
}
else if (r.Type.IsNullableType())
{
l = l.Type.IsNullableType() ? l : Expression.Convert(l, r.Type);
}

return Expression.Equal(l, r);
})
.Aggregate((a, b) => Expression.AndAlso(a, b));

static Expression RemoveObjectConvert(Expression expression)
=> expression is UnaryExpression unaryExpression
&& expression.Type == typeof(object)
&& expression.NodeType == ExpressionType.Convert
? unaryExpression.Operand
: expression;
}

private static bool IsNullConstantExpression(Expression expression)
=> expression is ConstantExpression constantExpression && constantExpression.Value == null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.EntityFrameworkCore.InMemory.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

Expand Down Expand Up @@ -457,6 +456,7 @@ private static (Expression, Expression) DecomposeJoinCondition(Expression joinCo
: (CreateAnonymousObject(leftExpressions), CreateAnonymousObject(rightExpressions))
: (null, null);

// InMemory joins need to use AnonymousObject to perform correct key comparison for server side joins
static Expression CreateAnonymousObject(List<Expression> expressions)
=> Expression.New(
AnonymousObject.AnonymousObjectCtor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,7 @@ private SqlExpression CreateJoinPredicate(
var outerKey = RemapLambdaBody(outer, outerKeySelector);
var innerKey = RemapLambdaBody(inner, innerKeySelector);

if (outerKey is NewExpression outerNew
&& outerNew.Type != typeof(AnonymousObject))
if (outerKey is NewExpression outerNew)
{
var innerNew = (NewExpression)innerKey;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;

Expand Down Expand Up @@ -237,10 +236,11 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));

if (binaryExpression.Left.Type == typeof(AnonymousObject)
if (binaryExpression.Left.Type == typeof(object[])
&& binaryExpression.Left is NewArrayExpression
&& binaryExpression.NodeType == ExpressionType.Equal)
{
return Visit(ConvertAnonymousObjectEqualityComparison(binaryExpression));
return Visit(ConvertObjectArrayEqualityComparison(binaryExpression));
}

var left = TryRemoveImplicitConvert(binaryExpression.Left);
Expand Down Expand Up @@ -768,10 +768,10 @@ private static Expression TryRemoveImplicitConvert(Expression expression)
return expression;
}

private static Expression ConvertAnonymousObjectEqualityComparison(BinaryExpression binaryExpression)
private static Expression ConvertObjectArrayEqualityComparison(BinaryExpression binaryExpression)
{
var leftExpressions = ((NewArrayExpression)((NewExpression)binaryExpression.Left).Arguments[0]).Expressions;
var rightExpressions = ((NewArrayExpression)((NewExpression)binaryExpression.Right).Arguments[0]).Expressions;
var leftExpressions = ((NewArrayExpression)binaryExpression.Left).Expressions;
var rightExpressions = ((NewArrayExpression)binaryExpression.Right).Expressions;

return leftExpressions.Zip(
rightExpressions,
Expand Down
12 changes: 5 additions & 7 deletions src/EFCore/Infrastructure/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,11 @@ public static Expression CreateKeyValueReadExpression(
bool makeNullable = false)
=> properties.Count == 1
? target.CreateEFPropertyExpression(properties[0], makeNullable)
: Expression.New(
AnonymousObject.AnonymousObjectCtor,
Expression.NewArrayInit(
typeof(object),
properties
.Select(p => Expression.Convert(target.CreateEFPropertyExpression(p, makeNullable), typeof(object)))
.Cast<Expression>()));
: Expression.NewArrayInit(
typeof(object),
properties
.Select(p => Expression.Convert(target.CreateEFPropertyExpression(p, makeNullable), typeof(object)))
.Cast<Expression>());

/// <summary>
/// <para>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ protected Expression ExpandNavigation(
// This is intentionally deferred to be applied to innerSource.Source
// Since outerKey's reference could change if a reference navigation is expanded afterwards
var predicateBody = Expression.AndAlso(
outerKey is NewExpression newExpression
&& newExpression.Arguments[0] is NewArrayExpression newArrayExpression
outerKey is NewArrayExpression newArrayExpression
? newArrayExpression.Expressions
.Select(e =>
{
Expand Down

0 comments on commit 58a07b6

Please sign in to comment.