From 3695bcab8373683e41cc7e5391669d6114f96dc3 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 26 Nov 2024 21:27:34 +0100 Subject: [PATCH] Correct VisitUnary operand evaluation in funcletizer (#35172) (#35203) Fixes #35152 (cherry picked from commit 3ba88c418276c5ba8264a29be42e0d5a39e92c94) --- .../Internal/ExpressionTreeFuncletizer.cs | 18 ++++++++++++++++-- .../NorthwindMiscellaneousQueryCosmosTest.cs | 11 +++++++++++ .../NorthwindMiscellaneousQueryTestBase.cs | 11 +++++++++++ ...NorthwindMiscellaneousQuerySqlServerTest.cs | 11 +++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index 7f48c98ca91..a04ef3b8888 100644 --- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -103,6 +103,9 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor private static readonly bool UseOldBehavior35095 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35095", out var enabled35095) && enabled35095; + 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!; @@ -1558,9 +1561,20 @@ UnaryExpression EvaluateOperand(UnaryExpression unary, Expression operand, State operand = ProcessEvaluatableRoot(operand, ref operandState); } - if (_state.ContainsEvaluatable) + if (UseOldBehavior35152) + { + if (_state.ContainsEvaluatable) + { + _state = _calculatingPath + ? State.CreateContainsEvaluatable( + typeof(UnaryExpression), + [_state.Path! with { PathFromParent = static e => Property(e, nameof(UnaryExpression.Operand)) }]) + : State.NoEvaluatability; + } + } + else { - _state = _calculatingPath + _state = operandState.ContainsEvaluatable && _calculatingPath ? State.CreateContainsEvaluatable( typeof(UnaryExpression), [_state.Path! with { PathFromParent = static e => Property(e, nameof(UnaryExpression.Operand)) }]) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs index 7884621121e..9d74bed0e7a 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs @@ -5284,6 +5284,17 @@ public override async Task Column_access_inside_subquery_predicate(bool async) AssertSql(); } + 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 3a8b3d94496..f0b8346fcb4 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -5856,4 +5856,15 @@ public virtual Task Column_access_inside_subquery_predicate(bool async) => AssertQuery( async, ss => ss.Set().Where(c => ss.Set().Where(o => c.CustomerID == "ALFKI").Any())); + + [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 76dfebfc240..839f136e9fc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -7476,6 +7476,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);