diff --git a/src/EFCore.Relational/Query/Internal/ParameterValueBasedSelectExpressionOptimizer.cs b/src/EFCore.Relational/Query/Internal/ParameterValueBasedSelectExpressionOptimizer.cs index 35069bcaff3..c3e2be51081 100644 --- a/src/EFCore.Relational/Query/Internal/ParameterValueBasedSelectExpressionOptimizer.cs +++ b/src/EFCore.Relational/Query/Internal/ParameterValueBasedSelectExpressionOptimizer.cs @@ -88,23 +88,21 @@ protected override Expression VisitExtension(Expression extensionExpression) var newPredicate = newSelectExpression.Predicate; var newHaving = newSelectExpression.Having; if (newSelectExpression.Predicate is SqlConstantExpression predicateConstantExpression - && predicateConstantExpression.Value is bool predicateBoolValue - && !predicateBoolValue) + && predicateConstantExpression.Value is bool predicateBoolValue) { changed = true; - newPredicate = SqlExpressionFactory.Equal( - predicateConstantExpression, - SqlExpressionFactory.Constant(true, predicateConstantExpression.TypeMapping)); + newPredicate = predicateBoolValue + ? null + : GenerateEqualTrueExpression(predicateConstantExpression); } if (newSelectExpression.Having is SqlConstantExpression havingConstantExpression - && havingConstantExpression.Value is bool havingBoolValue - && !havingBoolValue) + && havingConstantExpression.Value is bool havingBoolValue) { changed = true; - newHaving = SqlExpressionFactory.Equal( - havingConstantExpression, - SqlExpressionFactory.Constant(true, havingConstantExpression.TypeMapping)); + newHaving = havingBoolValue + ? null + : GenerateEqualTrueExpression(havingConstantExpression); } return changed @@ -122,7 +120,42 @@ protected override Expression VisitExtension(Expression extensionExpression) : newSelectExpression; } + if (newExpression is CaseExpression newCaseExpression + && newCaseExpression.Operand == null) + { + var changed = false; + var newWhenClauses = new List(); + for (var i = 0; i < newCaseExpression.WhenClauses.Count; i++) + { + if (newCaseExpression.WhenClauses[i].Test is SqlConstantExpression whenConstantTestExpression + && whenConstantTestExpression.Value is bool) + { + changed = true; + newWhenClauses.Add( + new CaseWhenClause( + GenerateEqualTrueExpression(whenConstantTestExpression), + newCaseExpression.WhenClauses[i].Result)); + } + else + { + newWhenClauses.Add(newCaseExpression.WhenClauses[i]); + } + } + + return changed + ? newCaseExpression.Update( + newCaseExpression.Operand, + newWhenClauses, + newCaseExpression.ElseResult) + : newCaseExpression; + } + return newExpression; + + SqlExpression GenerateEqualTrueExpression(SqlExpression argument) + => SqlExpressionFactory.Equal( + argument, + SqlExpressionFactory.Constant(true, argument.TypeMapping)); } protected override Expression VisitSqlUnaryExpression(SqlUnaryExpression sqlUnaryExpression) diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index fc0e77dd47d..9b624417614 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -7547,6 +7547,36 @@ public virtual Task FirstOrDefault_navigation_access_entity_equality_in_where_pr .Where(f => f.Capital == ss.Set().OrderBy(s => s.Nickname).FirstOrDefault().CityOfBirth)); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Conditional_expression_with_test_being_simplified_to_constant_simple(bool isAsync) + { + var prm = true; + var prm2 = (string)null; + + return AssertQuery( + isAsync, + ss => ss.Set().Where(g => g.HasSoulPatch == prm + ? true + : g.CityOfBirthName == prm2)); + } + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Conditional_expression_with_test_being_simplified_to_constant_complex(bool isAsync) + { + var prm = true; + var prm2 = "Dom's Lancer"; + var prm3 = (string)null; + + return AssertQuery( + isAsync, + ss => ss.Set().Where(g => g.HasSoulPatch == prm + ? ss.Set().Where(w => w.Id == g.SquadId).Single().Name == prm2 + : g.CityOfBirthName == prm3)); + } + protected async Task AssertTranslationFailed(Func testCode) { Assert.Contains( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 9c45d62ab35..3979f8e3299 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -7504,6 +7504,52 @@ WHERE [g].[Discriminator] IN (N'Gear', N'Officer') ORDER BY [g].[Nickname]) IS NULL))"); } + public override async Task Conditional_expression_with_test_being_simplified_to_constant_simple(bool isAsync) + { + await base.Conditional_expression_with_test_being_simplified_to_constant_simple(isAsync); + + AssertSql( + @"@__prm_0='True' + +SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] +FROM [Gears] AS [g] +WHERE [g].[Discriminator] IN (N'Gear', N'Officer') AND (CASE + WHEN [g].[HasSoulPatch] = @__prm_0 THEN CAST(1 AS bit) + ELSE CASE + WHEN CAST(0 AS bit) = CAST(1 AS bit) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END +END = CAST(1 AS bit))"); + } + + public override async Task Conditional_expression_with_test_being_simplified_to_constant_complex(bool isAsync) + { + await base.Conditional_expression_with_test_being_simplified_to_constant_complex(isAsync); + + AssertSql( + @"@__prm_0='True' +@__prm2_1='Dom's Lancer' (Size = 4000) + +SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] +FROM [Gears] AS [g] +WHERE [g].[Discriminator] IN (N'Gear', N'Officer') AND (CASE + WHEN [g].[HasSoulPatch] = @__prm_0 THEN CASE + WHEN (( + SELECT TOP(1) [w].[Name] + FROM [Weapons] AS [w] + WHERE [w].[Id] = [g].[SquadId]) = @__prm2_1) AND ( + SELECT TOP(1) [w].[Name] + FROM [Weapons] AS [w] + WHERE [w].[Id] = [g].[SquadId]) IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END + ELSE CASE + WHEN CAST(0 AS bit) = CAST(1 AS bit) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END +END = CAST(1 AS bit))"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }