From ffaaf228617e6c752bbc80762b5b8b94b64c574e Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 25 Nov 2020 14:05:00 -0800 Subject: [PATCH] Query: Throw error for translating Lambda right away LambdaExpression can never be translated. The only time when returning null has effect in translation is projection where we would visit the base to client eval. But lambda cannot be client evaluated either so right thing to do is just throw exception so that user know which lambda is throwing error. Resolves #18179 --- .../CosmosSqlTranslatingExpressionVisitor.cs | 2 +- ...yExpressionTranslatingExpressionVisitor.cs | 2 +- ...lationalSqlTranslatingExpressionVisitor.cs | 2 +- .../Query/NorthwindSelectQueryTestBase.cs | 64 +++++++++++++------ 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs index 32f8eede106..cbd5525d72d 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs @@ -325,7 +325,7 @@ protected override Expression VisitInvocation(InvocationExpression invocationExp /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override Expression VisitLambda(Expression lambdaExpression) - => null; + => throw new InvalidOperationException(CoreStrings.TranslationFailed(lambdaExpression.Print())); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs index ae7aeb95ebf..6d0d21742be 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs @@ -308,7 +308,7 @@ protected override Expression VisitInvocation(InvocationExpression invocationExp /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override Expression VisitLambda(Expression lambdaExpression) - => QueryCompilationContext.NotTranslatedExpression; + => throw new InvalidOperationException(CoreStrings.TranslationFailed(lambdaExpression.Print())); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 7610f46bc55..8c4d1705a73 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -432,7 +432,7 @@ protected override Expression VisitInvocation(InvocationExpression invocationExp /// protected override Expression VisitLambda(Expression lambdaExpression) - => QueryCompilationContext.NotTranslatedExpression; + => throw new InvalidOperationException(CoreStrings.TranslationFailed(lambdaExpression.Print())); /// protected override Expression VisitListInit(ListInitExpression listInitExpression) diff --git a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs index bc98fe8d958..0e29a6e7c01 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindSelectQueryTestBase.cs @@ -2001,26 +2001,52 @@ public virtual Task Projection_take_predicate_projection(bool async) [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Ternary_in_client_eval_assigns_correct_types(bool async) - { return AssertQuery( - async, - ss => ss.Set() + { + return AssertQuery( + async, + ss => ss.Set() - .Where(o => o.OrderID < 10300) - .OrderBy(e => e.OrderID) - .Select( - o => new - { - CustomerID = ClientMethod(o.CustomerID), - OrderDate = o.OrderDate.HasValue ? o.OrderDate.Value : new DateTime(o.OrderID - 10000, 1, 1), - OrderDate2 = o.OrderDate.HasValue == false ? new DateTime(o.OrderID - 10000, 1, 1) : o.OrderDate.Value - }), - assertOrder: true, - elementAsserter: (e, a) => - { - AssertEqual(e.CustomerID, a.CustomerID); - AssertEqual(e.OrderDate, a.OrderDate); - AssertEqual(e.OrderDate2, a.OrderDate2); - }); + .Where(o => o.OrderID < 10300) + .OrderBy(e => e.OrderID) + .Select( + o => new + { + CustomerID = ClientMethod(o.CustomerID), + OrderDate = o.OrderDate.HasValue ? o.OrderDate.Value : new DateTime(o.OrderID - 10000, 1, 1), + OrderDate2 = o.OrderDate.HasValue == false ? new DateTime(o.OrderID - 10000, 1, 1) : o.OrderDate.Value + }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.CustomerID, a.CustomerID); + AssertEqual(e.OrderDate, a.OrderDate); + AssertEqual(e.OrderDate2, a.OrderDate2); + }); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task VisitLambda_should_not_be_visited_trivially(bool async) + { + return AssertTranslationFailed(() => AssertQuery( + async, + ss => + { + var orders = ss.Set().Where(o => o.CustomerID.StartsWith("A")).ToList(); + + return ss.Set() + .Select(c => new + { + Customer = c, + HasOrder = orders.Any(o => o.CustomerID == c.CustomerID) + }); + }, + elementSorter: e => e.Customer.CustomerID, + elementAsserter: (e, a) => + { + AssertEqual(e.Customer, a.Customer); + AssertEqual(e.HasOrder, a.HasOrder); + })); } private static string ClientMethod(string s) => s;