From 8a6afcef3d0e0d463a6edae60f2507404ccbfcf8 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Fri, 5 Jul 2024 14:26:50 +0200 Subject: [PATCH] Translate `COALESCE` as `ISNULL` `COALESCE`is a syntactic shortcut for the CASE expression. As such, the input values are evaluated multiple times. `ISNULL` does not have this shortcoming. Fixes #32519. --- .../Internal/SqlServerQuerySqlGenerator.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index 717d31dad4f..64c4d282642 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -200,6 +200,43 @@ protected override Expression VisitValues(ValuesExpression valuesExpression) return valuesExpression; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override Expression VisitSqlFunction(SqlFunctionExpression sqlFunctionExpression) + { + if (sqlFunctionExpression is { IsBuiltIn: true, Arguments: not null } + && string.Equals(sqlFunctionExpression.Name, "COALESCE", StringComparison.OrdinalIgnoreCase)) + { + var head = sqlFunctionExpression.Arguments[0]; + if (head.TypeMapping != sqlFunctionExpression.TypeMapping) + { + head = new SqlUnaryExpression( + ExpressionType.Convert, + head, + sqlFunctionExpression.Type, + sqlFunctionExpression.TypeMapping); + } + + sqlFunctionExpression = (SqlFunctionExpression)sqlFunctionExpression + .Arguments + .Skip(1) + .Aggregate(head, (l, r) => new SqlFunctionExpression( + "ISNULL", + arguments: [l, r], + nullable: true, + argumentsPropagateNullability: [false, false], + sqlFunctionExpression.Type, + sqlFunctionExpression.TypeMapping + )); + } + + return base.VisitSqlFunction(sqlFunctionExpression); + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in