Skip to content

Commit fa4ce99

Browse files
authored
Add missing Converts when simplifying in funcletizer (#35122) (#35202)
Fixes #35095 (cherry picked from commit 3cae7a8)
1 parent a8c7b9d commit fa4ce99

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor
100100

101101
private static readonly IReadOnlySet<string> EmptyStringSet = new HashSet<string>();
102102

103+
private static readonly bool UseOldBehavior35095 =
104+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35095", out var enabled35095) && enabled35095;
105+
103106
private static readonly MethodInfo ReadOnlyCollectionIndexerGetter = typeof(ReadOnlyCollection<Expression>).GetProperties()
104107
.Single(p => p.GetIndexParameters() is { Length: 1 } indexParameters && indexParameters[0].ParameterType == typeof(int)).GetMethod!;
105108

@@ -379,17 +382,23 @@ protected override Expression VisitBinary(BinaryExpression binary)
379382
case ExpressionType.Coalesce:
380383
leftValue = Evaluate(left);
381384

385+
Expression returnValue;
382386
switch (leftValue)
383387
{
384388
case null:
385-
return Visit(binary.Right, out _state);
389+
returnValue = Visit(binary.Right, out _state);
390+
break;
386391
case bool b:
387392
_state = leftState with { StateType = StateType.EvaluatableWithoutCapturedVariable };
388-
return Constant(b);
393+
returnValue = Constant(b);
394+
break;
389395
default:
390-
return left;
396+
returnValue = left;
397+
break;
391398
}
392399

400+
return UseOldBehavior35095 ? returnValue : ConvertIfNeeded(returnValue, binary.Type);
401+
393402
case ExpressionType.OrElse or ExpressionType.AndAlso when Evaluate(left) is bool leftBoolValue:
394403
{
395404
left = Constant(leftBoolValue);
@@ -511,9 +520,10 @@ protected override Expression VisitConditional(ConditionalExpression conditional
511520
// If the test evaluates, simplify the conditional away by bubbling up the leg that remains
512521
if (testState.IsEvaluatable && Evaluate(test) is bool testBoolValue)
513522
{
514-
return testBoolValue
523+
var returnValue = testBoolValue
515524
? Visit(conditional.IfTrue, out _state)
516525
: Visit(conditional.IfFalse, out _state);
526+
return UseOldBehavior35095 ? returnValue : ConvertIfNeeded(returnValue, conditional.Type);
517527
}
518528

519529
var ifTrue = Visit(conditional.IfTrue, out var ifTrueState);
@@ -2101,6 +2111,9 @@ static Expression RemoveConvert(Expression expression)
21012111
}
21022112
}
21032113

2114+
private static Expression ConvertIfNeeded(Expression expression, Type type)
2115+
=> expression.Type == type ? expression : Convert(expression, type);
2116+
21042117
private bool IsGenerallyEvaluatable(Expression expression)
21052118
=> _evaluatableExpressionFilter.IsEvaluatableExpression(expression, _model)
21062119
&& (_parameterize

test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,18 @@ FROM root c
33033303
SELECT VALUE c["OrderID"]
33043304
FROM root c
33053305
WHERE ((c["$type"] = "Order") AND (c["OrderID"] = 10252))
3306+
""");
3307+
});
3308+
3309+
public override Task Simplifiable_coalesce_over_nullable(bool async)
3310+
=> Fixture.NoSyncTest(
3311+
async, async a =>
3312+
{
3313+
await base.Simplifiable_coalesce_over_nullable(a);
3314+
3315+
AssertSql(
3316+
"""
3317+
ReadItem(None, Order|10248)
33063318
""");
33073319
});
33083320

test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,7 +2536,18 @@ await AssertQuery(
25362536
elementAsserter: (e, a) => AssertEqual(e.Id, a.Id));
25372537
}
25382538

2539-
#region Evaluation order of predicates
2539+
[ConditionalTheory] // #35095
2540+
[MemberData(nameof(IsAsyncData))]
2541+
public virtual Task Simplifiable_coalesce_over_nullable(bool async)
2542+
{
2543+
int? orderId = 10248;
2544+
2545+
return AssertQuery(
2546+
async,
2547+
ss => ss.Set<Order>().Where(o => o.OrderID == (orderId ?? 0)));
2548+
}
2549+
2550+
#region Evaluation order of operators
25402551

25412552
[ConditionalTheory]
25422553
[MemberData(nameof(IsAsyncData))]
@@ -2559,5 +2570,5 @@ public virtual Task Take_and_Distinct_evaluation_order(bool async)
25592570
async,
25602571
ss => ss.Set<Customer>().Select(c => c.ContactTitle).OrderBy(t => t).Take(3).Distinct());
25612572

2562-
#endregion Evaluation order of predicates
2573+
#endregion Evaluation order of operators
25632574
}

test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3425,7 +3425,21 @@ FROM [Orders] AS [o]
34253425
""");
34263426
}
34273427

3428-
#region Evaluation order of predicates
3428+
public override async Task Simplifiable_coalesce_over_nullable(bool async)
3429+
{
3430+
await base.Simplifiable_coalesce_over_nullable(async);
3431+
3432+
AssertSql(
3433+
"""
3434+
@__p_0='10248'
3435+
3436+
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
3437+
FROM [Orders] AS [o]
3438+
WHERE [o].[OrderID] = @__p_0
3439+
""");
3440+
}
3441+
3442+
#region Evaluation order of operators
34293443

34303444
public override async Task Take_and_Where_evaluation_order(bool async)
34313445
{
@@ -3483,7 +3497,7 @@ ORDER BY [c].[ContactTitle]
34833497
""");
34843498
}
34853499

3486-
#endregion Evaluation order of predicates
3500+
#endregion Evaluation order of operators
34873501

34883502
private void AssertSql(params string[] expected)
34893503
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

0 commit comments

Comments
 (0)