From 126c618a56f69b39a77aafc7c75d74ccfecd5762 Mon Sep 17 00:00:00 2001 From: danevandy99 Date: Mon, 3 Jun 2024 18:38:17 -0500 Subject: [PATCH] Requested changes WIP --- ...yExpressionTranslatingExpressionVisitor.cs | 7 +--- .../Translators/EnumMethodTranslator.cs | 22 +++++++---- .../Query/GearsOfWarQuerySqlServerTest.cs | 38 +++++++++---------- .../Query/TPCGearsOfWarQuerySqlServerTest.cs | 38 +++++++++---------- .../Query/TPTGearsOfWarQuerySqlServerTest.cs | 38 +++++++++---------- .../TemporalGearsOfWarQuerySqlServerTest.cs | 38 +++++++++---------- 6 files changed, 92 insertions(+), 89 deletions(-) diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs index 6234496a5f2..a5c33c32481 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs @@ -882,13 +882,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } // if object is nullable, add null safeguard before calling the function - // we special-case Nullable<>.GetValueOrDefault, which doesn't need the safeguard, - // and Nullable<>.ToString when the object is a nullable value type. + // we special-case Nullable<>.GetValueOrDefault, which doesn't need the safeguard if (methodCallExpression.Object != null && @object!.Type.IsNullableType() - && methodCallExpression.Method.Name != nameof(Nullable.GetValueOrDefault) - && (!@object!.Type.IsNullableValueType() - || methodCallExpression.Method.Name != nameof(Nullable.ToString))) + && methodCallExpression.Method.Name != nameof(Nullable.GetValueOrDefault)) { var result = (Expression)methodCallExpression.Update( Expression.Convert(@object, methodCallExpression.Object.Type), diff --git a/src/EFCore.Relational/Query/Internal/Translators/EnumMethodTranslator.cs b/src/EFCore.Relational/Query/Internal/Translators/EnumMethodTranslator.cs index ef8811a8d0f..75a28885f7e 100644 --- a/src/EFCore.Relational/Query/Internal/Translators/EnumMethodTranslator.cs +++ b/src/EFCore.Relational/Query/Internal/Translators/EnumMethodTranslator.cs @@ -63,24 +63,30 @@ public EnumMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) if (converterType is not null && converterType.IsGenericType) { - if (converterType.GetGenericTypeDefinition() == typeof(EnumToNumberConverter<,>) - && converterType.GetGenericArguments().Length == 2 - && converterType.GetGenericArguments()[1] == typeof(int) - && (instance is SqlParameterExpression || instance is ColumnExpression)) + if (converterType.GetGenericTypeDefinition() == typeof(EnumToNumberConverter<,>)) { - var cases = Enum.GetValues(instance.Type) + var whenClauses = Enum.GetValues(instance.Type) .Cast() .Select(value => new CaseWhenClause( - _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(value)), + _sqlExpressionFactory.Constant(value), _sqlExpressionFactory.Constant(value.ToString(), typeof(string)))) .ToArray(); - return _sqlExpressionFactory.Case(cases, _sqlExpressionFactory.Constant(string.Empty, typeof(string))); + SqlExpression elseResult = _sqlExpressionFactory.Convert(instance, typeof(string)); + + if (instance is ColumnExpression { IsNullable: true }) + { + elseResult = _sqlExpressionFactory.Coalesce( + elseResult, + _sqlExpressionFactory.Constant(string.Empty, typeof(string))); + } + + return _sqlExpressionFactory.Case(instance, whenClauses, elseResult); } else if (converterType.GetGenericTypeDefinition() == typeof(EnumToStringConverter<>)) { // TODO: Unnecessary cast to string, #33733 - return _sqlExpressionFactory.MakeUnary(ExpressionType.Convert, instance, typeof(string)); + return _sqlExpressionFactory.Convert(instance, typeof(string)); } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index b4608877ef9..726e58c9b3d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -4023,17 +4023,17 @@ public override async Task ToString_enum_property_projection(bool async) AssertSql( """ -SELECT CASE - WHEN [g].[Rank] = 0 THEN N'None' - WHEN [g].[Rank] = 1 THEN N'Private' - WHEN [g].[Rank] = 2 THEN N'Corporal' - WHEN [g].[Rank] = 4 THEN N'Sergeant' - WHEN [g].[Rank] = 8 THEN N'Lieutenant' - WHEN [g].[Rank] = 16 THEN N'Captain' - WHEN [g].[Rank] = 32 THEN N'Major' - WHEN [g].[Rank] = 64 THEN N'Colonel' - WHEN [g].[Rank] = 128 THEN N'General' - ELSE N'' +SELECT CASE [g].[Rank] + WHEN 0 THEN N'None' + WHEN 1 THEN N'Private' + WHEN 2 THEN N'Corporal' + WHEN 4 THEN N'Sergeant' + WHEN 8 THEN N'Lieutenant' + WHEN 16 THEN N'Captain' + WHEN 32 THEN N'Major' + WHEN 64 THEN N'Colonel' + WHEN 128 THEN N'General' + ELSE CAST([g].[Rank] AS nvarchar(max)) END FROM [Gears] AS [g] """); @@ -4045,10 +4045,10 @@ public override async Task ToString_nullable_enum_property_projection(bool async AssertSql( """ -SELECT CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +SELECT CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE COALESCE(CAST([w].[AmmunitionType] AS nvarchar(max)), N'') END FROM [Weapons] AS [w] """); @@ -4074,10 +4074,10 @@ public override async Task ToString_nullable_enum_contains(bool async) """ SELECT [w].[Name] FROM [Weapons] AS [w] -WHERE CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +WHERE CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE CAST([w].[AmmunitionType] AS nvarchar(max)) END LIKE N'%Cart%' """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index 39800d18473..9af7df9eb3d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -5386,17 +5386,17 @@ public override async Task ToString_enum_property_projection(bool async) AssertSql( """ -SELECT CASE - WHEN [u].[Rank] = 0 THEN N'None' - WHEN [u].[Rank] = 1 THEN N'Private' - WHEN [u].[Rank] = 2 THEN N'Corporal' - WHEN [u].[Rank] = 4 THEN N'Sergeant' - WHEN [u].[Rank] = 8 THEN N'Lieutenant' - WHEN [u].[Rank] = 16 THEN N'Captain' - WHEN [u].[Rank] = 32 THEN N'Major' - WHEN [u].[Rank] = 64 THEN N'Colonel' - WHEN [u].[Rank] = 128 THEN N'General' - ELSE N'' +SELECT CASE [u].[Rank] + WHEN 0 THEN N'None' + WHEN 1 THEN N'Private' + WHEN 2 THEN N'Corporal' + WHEN 4 THEN N'Sergeant' + WHEN 8 THEN N'Lieutenant' + WHEN 16 THEN N'Captain' + WHEN 32 THEN N'Major' + WHEN 64 THEN N'Colonel' + WHEN 128 THEN N'General' + ELSE CAST([u].[Rank] AS nvarchar(max)) END FROM ( SELECT [g].[Rank] @@ -5414,10 +5414,10 @@ public override async Task ToString_nullable_enum_property_projection(bool async AssertSql( """ -SELECT CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +SELECT CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE COALESCE(CAST([w].[AmmunitionType] AS nvarchar(max)), N'') END FROM [Weapons] AS [w] """); @@ -5443,10 +5443,10 @@ public override async Task ToString_nullable_enum_contains(bool async) """ SELECT [w].[Name] FROM [Weapons] AS [w] -WHERE CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +WHERE CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE CAST([w].[AmmunitionType] AS nvarchar(max)) END LIKE N'%Cart%' """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 0f16962617f..6a2e9e0ff63 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -4672,17 +4672,17 @@ public override async Task ToString_enum_property_projection(bool async) AssertSql( """ -SELECT CASE - WHEN [g].[Rank] = 0 THEN N'None' - WHEN [g].[Rank] = 1 THEN N'Private' - WHEN [g].[Rank] = 2 THEN N'Corporal' - WHEN [g].[Rank] = 4 THEN N'Sergeant' - WHEN [g].[Rank] = 8 THEN N'Lieutenant' - WHEN [g].[Rank] = 16 THEN N'Captain' - WHEN [g].[Rank] = 32 THEN N'Major' - WHEN [g].[Rank] = 64 THEN N'Colonel' - WHEN [g].[Rank] = 128 THEN N'General' - ELSE N'' +SELECT CASE [g].[Rank] + WHEN 0 THEN N'None' + WHEN 1 THEN N'Private' + WHEN 2 THEN N'Corporal' + WHEN 4 THEN N'Sergeant' + WHEN 8 THEN N'Lieutenant' + WHEN 16 THEN N'Captain' + WHEN 32 THEN N'Major' + WHEN 64 THEN N'Colonel' + WHEN 128 THEN N'General' + ELSE CAST([g].[Rank] AS nvarchar(max)) END FROM [Gears] AS [g] """); @@ -4694,10 +4694,10 @@ public override async Task ToString_nullable_enum_property_projection(bool async AssertSql( """ -SELECT CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +SELECT CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE COALESCE(CAST([w].[AmmunitionType] AS nvarchar(max)), N'') END FROM [Weapons] AS [w] """); @@ -4723,10 +4723,10 @@ public override async Task ToString_nullable_enum_contains(bool async) """ SELECT [w].[Name] FROM [Weapons] AS [w] -WHERE CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +WHERE CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE CAST([w].[AmmunitionType] AS nvarchar(max)) END LIKE N'%Cart%' """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index f8cb0d36f65..2f5e4d6ccb8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -4799,17 +4799,17 @@ public override async Task ToString_enum_property_projection(bool async) AssertSql( """ -SELECT CASE - WHEN [g].[Rank] = 0 THEN N'None' - WHEN [g].[Rank] = 1 THEN N'Private' - WHEN [g].[Rank] = 2 THEN N'Corporal' - WHEN [g].[Rank] = 4 THEN N'Sergeant' - WHEN [g].[Rank] = 8 THEN N'Lieutenant' - WHEN [g].[Rank] = 16 THEN N'Captain' - WHEN [g].[Rank] = 32 THEN N'Major' - WHEN [g].[Rank] = 64 THEN N'Colonel' - WHEN [g].[Rank] = 128 THEN N'General' - ELSE N'' +SELECT CASE [g].[Rank] + WHEN 0 THEN N'None' + WHEN 1 THEN N'Private' + WHEN 2 THEN N'Corporal' + WHEN 4 THEN N'Sergeant' + WHEN 8 THEN N'Lieutenant' + WHEN 16 THEN N'Captain' + WHEN 32 THEN N'Major' + WHEN 64 THEN N'Colonel' + WHEN 128 THEN N'General' + ELSE CAST([g].[Rank] AS nvarchar(max)) END FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] """); @@ -4821,10 +4821,10 @@ public override async Task ToString_nullable_enum_property_projection(bool async AssertSql( """ -SELECT CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +SELECT CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE COALESCE(CAST([w].[AmmunitionType] AS nvarchar(max)), N'') END FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] """); @@ -4850,10 +4850,10 @@ public override async Task ToString_nullable_enum_contains(bool async) """ SELECT [w].[Name] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] -WHERE CASE - WHEN [w].[AmmunitionType] = 1 THEN N'Cartridge' - WHEN [w].[AmmunitionType] = 2 THEN N'Shell' - ELSE N'' +WHERE CASE [w].[AmmunitionType] + WHEN 1 THEN N'Cartridge' + WHEN 2 THEN N'Shell' + ELSE CAST([w].[AmmunitionType] AS nvarchar(max)) END LIKE N'%Cart%' """); }