diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs index 6eef2786a8e..30ab9fb03ee 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs @@ -976,7 +976,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] string arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -987,7 +987,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, bool? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -998,7 +998,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, double? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1009,7 +1009,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, decimal? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1020,7 +1020,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, DateTime? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1031,7 +1031,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, TimeSpan? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1042,7 +1042,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, DateTimeOffset? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1053,7 +1053,7 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, [CanBeNull] byte[] arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); /// /// Returns the number of bytes used to represent any expression. @@ -1064,6 +1064,6 @@ public static TimeSpan TimeFromParts( public static int? DataLength( [CanBeNull] this DbFunctions _, Guid? arg) - => throw new InvalidOperationException(SqlServerStrings.FunctionOnClient(nameof(DataLength))); + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DataLength))); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs index 4a8660f920e..7915666a7e5 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerDataLengthFunctionTranslator.cs @@ -14,6 +14,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal { public class SqlServerDataLengthFunctionTranslator : IMethodCallTranslator { + private static readonly List _longReturningTypes = new List { "nvarchar(max)", "varchar(max)", "varbinary(max)" }; + private static readonly HashSet _methodInfoDataLengthMapping = new HashSet { @@ -68,6 +70,24 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method if (_methodInfoDataLengthMapping.Contains(method)) { + var argument = arguments[1]; + if (argument.TypeMapping == null) + { + argument = _sqlExpressionFactory.ApplyDefaultTypeMapping(argument); + } + + if (_longReturningTypes.Contains(argument.TypeMapping.StoreType)) + { + var result = _sqlExpressionFactory.Function( + "DATALENGTH", + arguments.Skip(1), + nullable: true, + argumentsPropagateNullability: new[] { true }, + typeof(long)); + + return _sqlExpressionFactory.Convert(result, method.ReturnType); + } + return _sqlExpressionFactory.Function( "DATALENGTH", arguments.Skip(1), diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d30fafa4a1a..a7cb439b0d3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -2,7 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel; +using Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query @@ -6976,6 +6979,20 @@ FROM [Gears] AS [g] ORDER BY [g].[Nickname]"); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public async Task DataLength_function_for_string_parameter(bool async) + { + await AssertQueryScalar( + async, + ss => ss.Set().Select(m => EF.Functions.DataLength(m.CodeName)), + ss => ss.Set().Select(m => (int?)(m.CodeName.Length * 2))); + + AssertSql( + @"SELECT CAST(DATALENGTH([m].[CodeName]) AS int) +FROM [Missions] AS [m]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); }