From 17b36d3f383b3e90e7e6f4344eb71488a4450b7c Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Mon, 25 Nov 2024 17:06:49 +0100 Subject: [PATCH] Correct VisitUnary operand evaluation in funcletizer (#35172) Fixes #35152 (cherry picked from commit 3ba88c418276c5ba8264a29be42e0d5a39e92c94) --- .../Query/Internal/ExpressionTreeFuncletizer.cs | 13 ++++++++++++- .../Query/NorthwindMiscellaneousQueryCosmosTest.cs | 11 +++++++++++ .../Query/NorthwindMiscellaneousQueryTestBase.cs | 11 +++++++++++ .../NorthwindMiscellaneousQuerySqlServerTest.cs | 11 +++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index d301f12df10..a8022145e86 100644 --- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -100,6 +100,9 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor private static readonly IReadOnlySet EmptyStringSet = new HashSet(); + private static readonly bool UseOldBehavior35152 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35152", out var enabled35152) && enabled35152; + private static readonly MethodInfo ReadOnlyCollectionIndexerGetter = typeof(ReadOnlyCollection).GetProperties() .Single(p => p.GetIndexParameters() is { Length: 1 } indexParameters && indexParameters[0].ParameterType == typeof(int)).GetMethod!; @@ -1548,7 +1551,7 @@ UnaryExpression EvaluateOperand(UnaryExpression unary, Expression operand, State operand = ProcessEvaluatableRoot(operand, ref operandState); } - if (_state.ContainsEvaluatable) + if (UseOldBehavior35152) { _state = _calculatingPath ? State.CreateContainsEvaluatable( @@ -1556,6 +1559,14 @@ UnaryExpression EvaluateOperand(UnaryExpression unary, Expression operand, State [_state.Path! with { PathFromParent = static e => Property(e, nameof(UnaryExpression.Operand)) }]) : State.NoEvaluatability; } + else + { + _state = operandState.ContainsEvaluatable && _calculatingPath + ? State.CreateContainsEvaluatable( + typeof(UnaryExpression), + [_state.Path! with { PathFromParent = static e => Property(e, nameof(UnaryExpression.Operand)) }]) + : State.NoEvaluatability; + } return unary.Update(operand); } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs index 2b0278d4aa2..1db80ffaf3f 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs @@ -5276,6 +5276,17 @@ public virtual async Task ToPageAsync_in_subquery_throws() #endregion ToPageAsync + public override async Task Cast_to_object_over_parameter_directly_in_lambda(bool async) + { + // Sync always throws before getting to exception being tested. + if (async) + { + // Cosmos doesn't support ORDER BY over parameter/constant: + // Unsupported ORDER BY clause. ORDER BY item expression could not be mapped to a document path. + await Assert.ThrowsAsync(() => base.Cast_to_object_over_parameter_directly_in_lambda(async: true)); + } + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs index b04c427891e..99f6aa74409 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -5849,4 +5849,15 @@ public virtual Task Static_member_access_gets_parameterized_within_larger_evalua private static string StaticProperty => "ALF"; + + [ConditionalTheory] // #35152 + [MemberData(nameof(IsAsyncData))] + public virtual Task Cast_to_object_over_parameter_directly_in_lambda(bool async) + { + var i = 8; + + return AssertQuery( + async, + ss => ss.Set().OrderBy(o => (object)i).Select(o => o)); + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index ddad65e8400..cd1b1b6b99b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -7461,6 +7461,17 @@ FROM [Orders] AS [o] """); } + public override async Task Cast_to_object_over_parameter_directly_in_lambda(bool async) + { + await base.Cast_to_object_over_parameter_directly_in_lambda(async); + + AssertSql( + """ +SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +"""); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);