Skip to content

Commit

Permalink
Query: Support InvocationExpression
Browse files Browse the repository at this point in the history
Code follows the simplification ReLinq did for us.

Resolves #17791
  • Loading branch information
smitpatel committed Oct 16, 2019
1 parent f25c799 commit 9d1279a
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;

namespace Microsoft.EntityFrameworkCore.Query.Internal
{
public class InvocationExpressionRemovingExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitInvocation(InvocationExpression invocationExpression)
{
var invokedExpression = StripTrivialConversions(invocationExpression.Expression);

return invokedExpression is LambdaExpression lambdaExpression
? InlineLambdaExpression(lambdaExpression, invocationExpression.Arguments)
: base.VisitInvocation(invocationExpression);
}

private Expression StripTrivialConversions(Expression expression)
{
while (expression is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& expression.Type == unaryExpression.Operand.Type
&& unaryExpression.Method == null)
{
expression = unaryExpression.Operand;
}

return expression;
}

private Expression InlineLambdaExpression(LambdaExpression lambdaExpression, ReadOnlyCollection<Expression> arguments)
{
var replacements = new Dictionary<Expression, Expression>(arguments.Count);
for (var i = 0; i < lambdaExpression.Parameters.Count; i++)
{
replacements.Add(lambdaExpression.Parameters[i], arguments[i]);
}

return new ReplacingExpressionVisitor(replacements).Visit(lambdaExpression.Body);
}
}
}
1 change: 1 addition & 0 deletions src/EFCore/Query/QueryTranslationPreprocessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public virtual Expression Process(Expression query)
{
query = new EnumerableToQueryableMethodConvertingExpressionVisitor().Visit(query);
query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query);
query = new InvocationExpressionRemovingExpressionVisitor().Visit(query);
query = new AllAnyToContainsRewritingExpressionVisitor().Visit(query);
query = new GroupJoinFlatteningExpressionVisitor().Visit(query);
query = new NullCheckRemovingExpressionVisitor().Visit(query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1515,9 +1515,20 @@ FROM root c
WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""Fax""] = null))");
}

public override async Task Where_expression_invoke(bool isAsync)
public override async Task Where_expression_invoke_1(bool isAsync)
{
await base.Where_expression_invoke(isAsync);
await base.Where_expression_invoke_1(isAsync);

AssertSql(
@"SELECT c
FROM root c
WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""CustomerID""] = ""ALFKI""))");
}

[ConditionalTheory(Skip = "Issue #17246")]
public override async Task Where_expression_invoke_2(bool isAsync)
{
await base.Where_expression_invoke_2(isAsync);

AssertSql(
@"SELECT c
Expand Down
20 changes: 18 additions & 2 deletions test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Where.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1495,9 +1495,9 @@ public virtual Task Where_default(bool isAsync)
entryCount: 22);
}

[ConditionalTheory(Skip = "Issue#14572")]
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_expression_invoke(bool isAsync)
public virtual Task Where_expression_invoke_1(bool isAsync)
{
Expression<Func<Customer, bool>> expression = c => c.CustomerID == "ALFKI";
var parameter = Expression.Parameter(typeof(Customer), "c");
Expand All @@ -1509,6 +1509,22 @@ public virtual Task Where_expression_invoke(bool isAsync)
entryCount: 1);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_expression_invoke_2(bool isAsync)
{
Expression<Func<Order, Customer>> customer = o => o.Customer;
Expression<Func<Customer, bool>> predicate = c => c.CustomerID == "ALFKI";
var exp = Expression.Lambda<Func<Order, bool>>(
Expression.Invoke(predicate, customer.Body),
customer.Parameters);

return AssertQuery(
isAsync,
ss => ss.Set<Order>().Where(exp),
entryCount: 6);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_concat_string_int_comparison1(bool isAsync)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1270,16 +1270,27 @@ FROM [Customers] AS [c]
WHERE [c].[Fax] IS NULL");
}

public override async Task Where_expression_invoke(bool isAsync)
public override async Task Where_expression_invoke_1(bool isAsync)
{
await base.Where_expression_invoke(isAsync);
await base.Where_expression_invoke_1(isAsync);

AssertSql(
@"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] = N'ALFKI'");
}

public override async Task Where_expression_invoke_2(bool isAsync)
{
await base.Where_expression_invoke_2(isAsync);

AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID]
WHERE ([c].[CustomerID] = N'ALFKI') AND [c].[CustomerID] IS NOT NULL");
}

public override async Task Where_concat_string_int_comparison1(bool isAsync)
{
await base.Where_concat_string_int_comparison1(isAsync);
Expand Down

0 comments on commit 9d1279a

Please sign in to comment.