diff --git a/.editorconfig b/.editorconfig index 691c78315df..4f76e64c9c0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -214,14 +214,6 @@ dotnet_naming_rule.type_parameter_naming.symbols = type_parameter_symbol dotnet_naming_rule.type_parameter_naming.style = type_parameter_style dotnet_naming_rule.type_parameter_naming.severity = suggestion -# Private Fields -dotnet_naming_symbols.private_field_symbol.applicable_kinds = field -dotnet_naming_symbols.private_field_symbol.applicable_accessibilities = private - -dotnet_naming_rule.private_field_naming.symbols = private_field_symbol -dotnet_naming_rule.private_field_naming.style = _camelCase -dotnet_naming_rule.private_field_naming.severity = suggestion - # Visible Fields dotnet_naming_symbols.public_field_symbol.applicable_kinds = field dotnet_naming_symbols.public_field_symbol.applicable_accessibilities = public, internal, protected, protected_internal, private_protected @@ -230,6 +222,23 @@ dotnet_naming_rule.public_field_naming.symbols = public_field_symbol dotnet_naming_rule.public_field_naming.style = pascal_case_style dotnet_naming_rule.public_field_naming.severity = suggestion +# Private constant Fields +dotnet_naming_symbols.const_field_symbol.applicable_kinds = field +dotnet_naming_symbols.const_field_symbol.applicable_accessibilities = private +dotnet_naming_symbols.const_field_symbol.required_modifiers = const + +dotnet_naming_rule.const_field_naming.symbols = const_field_symbol +dotnet_naming_rule.const_field_naming.style = pascal_case_style +dotnet_naming_rule.const_field_naming.severity = suggestion + +# Private Fields +dotnet_naming_symbols.private_field_symbol.applicable_kinds = field +dotnet_naming_symbols.private_field_symbol.applicable_accessibilities = private + +dotnet_naming_rule.private_field_naming.symbols = private_field_symbol +dotnet_naming_rule.private_field_naming.style = _camelCase +dotnet_naming_rule.private_field_naming.severity = suggestion + # Parameters dotnet_naming_symbols.parameter_symbol.applicable_kinds = parameter dotnet_naming_symbols.parameter_symbol.applicable_accessibilities = * diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerObjectToStringTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerObjectToStringTranslator.cs index 5b060d01906..1f0617bb982 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerObjectToStringTranslator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerObjectToStringTranslator.cs @@ -18,23 +18,23 @@ public class SqlServerObjectToStringTranslator : IMethodCallTranslator private static readonly Dictionary _typeMapping = new Dictionary { - { typeof(int), "VARCHAR(11)" }, - { typeof(long), "VARCHAR(20)" }, - { typeof(DateTime), $"VARCHAR({DefaultLength})" }, - { typeof(Guid), "VARCHAR(36)" }, + { typeof(sbyte), "VARCHAR(4)" }, { typeof(byte), "VARCHAR(3)" }, - { typeof(byte[]), $"VARCHAR({DefaultLength})" }, - { typeof(double), $"VARCHAR({DefaultLength})" }, - { typeof(DateTimeOffset), $"VARCHAR({DefaultLength})" }, - { typeof(char), "VARCHAR(1)" }, { typeof(short), "VARCHAR(6)" }, + { typeof(ushort), "VARCHAR(5)" }, + { typeof(int), "VARCHAR(11)" }, + { typeof(uint), "VARCHAR(10)" }, + { typeof(long), "VARCHAR(20)" }, + { typeof(ulong), "VARCHAR(20)" }, { typeof(float), $"VARCHAR({DefaultLength})" }, + { typeof(double), $"VARCHAR({DefaultLength})" }, { typeof(decimal), $"VARCHAR({DefaultLength})" }, + { typeof(char), "VARCHAR(1)" }, + { typeof(DateTime), $"VARCHAR({DefaultLength})" }, + { typeof(DateTimeOffset), $"VARCHAR({DefaultLength})" }, { typeof(TimeSpan), $"VARCHAR({DefaultLength})" }, - { typeof(uint), "VARCHAR(10)" }, - { typeof(ushort), "VARCHAR(5)" }, - { typeof(ulong), "VARCHAR(19)" }, - { typeof(sbyte), "VARCHAR(4)" } + { typeof(Guid), "VARCHAR(36)" }, + { typeof(byte[]), $"VARCHAR({DefaultLength})" }, }; private readonly ISqlExpressionFactory _sqlExpressionFactory; @@ -52,16 +52,14 @@ public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method return method.Name == nameof(ToString) && arguments.Count == 0 && instance != null - && _typeMapping.TryGetValue( - instance.Type.UnwrapNullableType(), - out var storeType) - ? _sqlExpressionFactory.Function( - "CONVERT", - new[] { _sqlExpressionFactory.Fragment(storeType), instance }, - nullable: true, - argumentsPropagateNullability: new bool[] { false, true }, - typeof(string)) - : null; + && _typeMapping.TryGetValue(instance.Type.UnwrapNullableType(), out var storeType) + ? _sqlExpressionFactory.Function( + "CONVERT", + new[] { _sqlExpressionFactory.Fragment(storeType), instance }, + nullable: true, + argumentsPropagateNullability: new bool[] { false, true }, + typeof(string)) + : null; } } } diff --git a/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs index 1548ea58fa0..7315d22096b 100644 --- a/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs @@ -95,6 +95,17 @@ public override void Can_read_back_bool_mapped_as_int_through_navigation() base.Can_read_back_bool_mapped_as_int_through_navigation(); } + [ConditionalFact(Skip = "Issue #20543")] + public override void Object_to_string_conversion() + { + base.Object_to_string_conversion(); + + AssertSql(@" "); + } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); + public class BuiltInDataTypesCosmosFixture : BuiltInDataTypesFixtureBase { protected override ITestStoreFactory TestStoreFactory => CosmosTestStoreFactory.Instance; @@ -113,6 +124,8 @@ public class BuiltInDataTypesCosmosFixture : BuiltInDataTypesFixtureBase public override bool SupportsDecimalComparisons => true; + public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory; + public override DateTime DefaultDateTime => new DateTime(); protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) diff --git a/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs index ee86a2a9d6e..bc2f44d1bfa 100644 --- a/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs @@ -171,6 +171,14 @@ FROM root c WHERE (c[""Discriminator""] IN (""Blog"", ""RssBlog"") AND NOT((c[""IndexerVisible""] = ""Aye"")))"); } + [ConditionalFact(Skip = "Issue #20543")] + public override void Object_to_string_conversion() + { + base.Object_to_string_conversion(); + + AssertSql(@" "); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs b/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs index ef2647b109a..7f05ced6eae 100644 --- a/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs +++ b/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs @@ -1994,6 +1994,69 @@ public virtual void Can_read_back_bool_mapped_as_int_through_navigation() Assert.True(result.BoolField); } + [ConditionalFact] + public virtual void Object_to_string_conversion() + { + using var context = CreateContext(); + var expected = context.Set() + .Where(e => e.Id == 13) + .AsEnumerable() + .Select(b => new + { + Sbyte = b.TestSignedByte.ToString(), + Byte = b.TestByte.ToString(), + Short = b.TestInt16.ToString(), + Ushort = b.TestUnsignedInt16.ToString(), + Int = b.TestInt32.ToString(), + Uint = b.TestUnsignedInt32.ToString(), + Long = b.TestInt64.ToString(), + Ulong = b.TestUnsignedInt64.ToString(), + Float = b.TestSingle.ToString(), + Double = b.TestDouble.ToString(), + Decimal = b.TestDecimal.ToString(), + Char = b.TestCharacter.ToString(), + DateTime = b.TestDateTime.ToString(), + DateTimeOffset = b.TestDateTimeOffset.ToString(), + TimeSpan = b.TestTimeSpan.ToString(), + }) + .First(); + + var query = context.Set() + .Where(e => e.Id == 13) + .Select(b => new + { + Sbyte = b.TestSignedByte.ToString(), + Byte = b.TestByte.ToString(), + Short = b.TestInt16.ToString(), + Ushort = b.TestUnsignedInt16.ToString(), + Int = b.TestInt32.ToString(), + Uint = b.TestUnsignedInt32.ToString(), + Long = b.TestInt64.ToString(), + Ulong = b.TestUnsignedInt64.ToString(), + Float = b.TestSingle.ToString(), + Double = b.TestDouble.ToString(), + Decimal = b.TestDecimal.ToString(), + Char = b.TestCharacter.ToString(), + DateTime = b.TestDateTime.ToString(), + DateTimeOffset = b.TestDateTimeOffset.ToString(), + TimeSpan = b.TestTimeSpan.ToString(), + }) + .ToList(); + + var actual = Assert.Single(query); + Assert.Equal(expected.Sbyte, actual.Sbyte); + Assert.Equal(expected.Byte, actual.Byte); + Assert.Equal(expected.Short, actual.Short); + Assert.Equal(expected.Ushort, actual.Ushort); + Assert.Equal(expected.Int, actual.Int); + Assert.Equal(expected.Uint, actual.Uint); + Assert.Equal(expected.Long, actual.Long); + Assert.Equal(expected.Ulong, actual.Ulong); + Assert.Equal(expected.Decimal, actual.Decimal); + Assert.Equal(expected.Char, actual.Char); + Assert.Equal(expected.TimeSpan, actual.TimeSpan); + } + public abstract class BuiltInDataTypesFixtureBase : SharedStoreFixtureBase { protected override string StoreName { get; } = "BuiltInDataTypes"; diff --git a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs index 501ed7a9057..84d52a3bdae 100644 --- a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs +++ b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs @@ -603,7 +603,6 @@ public virtual void Collection_enum_as_string_Contains() Assert.Throws( () => context.Set().Where(e => e.Roles.Contains(sameRole)).ToList()) .Message.Replace("\r", "").Replace("\n", "")); - } protected class CollectionEnum diff --git a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs index 1aa390f316c..51a920c0d80 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs @@ -2915,6 +2915,20 @@ public void Can_get_column_types_from_built_model() } } + public override void Object_to_string_conversion() + { + base.Object_to_string_conversion(); + + AssertSql( + @"SELECT [b].[Id], [b].[Enum16], [b].[Enum32], [b].[Enum64], [b].[Enum8], [b].[EnumS8], [b].[EnumU16], [b].[EnumU32], [b].[EnumU64], [b].[PartitionId], [b].[TestBoolean], [b].[TestByte], [b].[TestCharacter], [b].[TestDateTime], [b].[TestDateTimeOffset], [b].[TestDecimal], [b].[TestDouble], [b].[TestInt16], [b].[TestInt32], [b].[TestInt64], [b].[TestSignedByte], [b].[TestSingle], [b].[TestTimeSpan], [b].[TestUnsignedInt16], [b].[TestUnsignedInt32], [b].[TestUnsignedInt64] +FROM [BuiltInDataTypes] AS [b] +WHERE [b].[Id] = 13", + // + @"SELECT CONVERT(VARCHAR(4), [b].[TestSignedByte]) AS [Sbyte], CONVERT(VARCHAR(3), [b].[TestByte]) AS [Byte], CONVERT(VARCHAR(6), [b].[TestInt16]) AS [Short], CONVERT(VARCHAR(5), [b].[TestUnsignedInt16]) AS [Ushort], CONVERT(VARCHAR(11), [b].[TestInt32]) AS [Int], CONVERT(VARCHAR(10), [b].[TestUnsignedInt32]) AS [Uint], CONVERT(VARCHAR(20), [b].[TestInt64]) AS [Long], CONVERT(VARCHAR(20), [b].[TestUnsignedInt64]) AS [Ulong], CONVERT(VARCHAR(100), [b].[TestSingle]) AS [Float], CONVERT(VARCHAR(100), [b].[TestDouble]) AS [Double], CONVERT(VARCHAR(100), [b].[TestDecimal]) AS [Decimal], CONVERT(VARCHAR(1), [b].[TestCharacter]) AS [Char], CONVERT(VARCHAR(100), [b].[TestDateTime]) AS [DateTime], CONVERT(VARCHAR(100), [b].[TestDateTimeOffset]) AS [DateTimeOffset], CONVERT(VARCHAR(100), [b].[TestTimeSpan]) AS [TimeSpan] +FROM [BuiltInDataTypes] AS [b] +WHERE [b].[Id] = 13"); + } + public static string QueryForColumnTypes(DbContext context, params string[] tablesToIgnore) { const string query diff --git a/test/EFCore.SqlServer.FunctionalTests/ConvertToProviderTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/ConvertToProviderTypesSqlServerTest.cs index d858fb49161..78d856b2df1 100644 --- a/test/EFCore.SqlServer.FunctionalTests/ConvertToProviderTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/ConvertToProviderTypesSqlServerTest.cs @@ -164,6 +164,11 @@ public virtual void Columns_have_expected_data_types() Assert.Equal(expected, actual, ignoreLineEndingDifferences: true); } + public override void Object_to_string_conversion() + { + // Return values are not string + } + public class ConvertToProviderTypesSqlServerFixture : ConvertToProviderTypesFixtureBase { public override bool StrictEquality => true; diff --git a/test/EFCore.SqlServer.FunctionalTests/CustomConvertersSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/CustomConvertersSqlServerTest.cs index f8b294e5c66..5dfc070f089 100644 --- a/test/EFCore.SqlServer.FunctionalTests/CustomConvertersSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/CustomConvertersSqlServerTest.cs @@ -255,6 +255,11 @@ FROM [Blog] AS [b] WHERE [b].[IndexerVisible] <> N'Aye'"); } + public override void Object_to_string_conversion() + { + // Return values are not string + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs index c057c7dedf0..8a68e987538 100644 --- a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs @@ -178,6 +178,11 @@ public override void Can_read_back_bool_mapped_as_int_through_navigation() // Column is mapped as int rather than byte[] } + public override void Object_to_string_conversion() + { + // Return values are string which byte[] cannot read + } + public class EverythingIsBytesSqlServerFixture : BuiltInDataTypesFixtureBase { public override bool StrictEquality => true; diff --git a/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs index 9e7923e6652..e6bc0665d6c 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BuiltInDataTypesSqliteTest.cs @@ -1496,6 +1496,20 @@ public virtual void Cant_query_ThenBy_of_converted_types() Assert.Equal(SqliteStrings.OrderByNotSupported("ulong"), ex.Message); } + public override void Object_to_string_conversion() + { + base.Object_to_string_conversion(); + + AssertSql( + @"SELECT ""b"".""Id"", ""b"".""Enum16"", ""b"".""Enum32"", ""b"".""Enum64"", ""b"".""Enum8"", ""b"".""EnumS8"", ""b"".""EnumU16"", ""b"".""EnumU32"", ""b"".""EnumU64"", ""b"".""PartitionId"", ""b"".""TestBoolean"", ""b"".""TestByte"", ""b"".""TestCharacter"", ""b"".""TestDateTime"", ""b"".""TestDateTimeOffset"", ""b"".""TestDecimal"", ""b"".""TestDouble"", ""b"".""TestInt16"", ""b"".""TestInt32"", ""b"".""TestInt64"", ""b"".""TestSignedByte"", ""b"".""TestSingle"", ""b"".""TestTimeSpan"", ""b"".""TestUnsignedInt16"", ""b"".""TestUnsignedInt32"", ""b"".""TestUnsignedInt64"" +FROM ""BuiltInDataTypes"" AS ""b"" +WHERE ""b"".""Id"" = 13", + // + @"SELECT ""b"".""TestSignedByte"", ""b"".""TestByte"", ""b"".""TestInt16"", ""b"".""TestUnsignedInt16"", ""b"".""TestInt32"", ""b"".""TestUnsignedInt32"", ""b"".""TestInt64"", ""b"".""TestUnsignedInt64"", ""b"".""TestSingle"", ""b"".""TestDouble"", ""b"".""TestDecimal"", ""b"".""TestCharacter"", ""b"".""TestDateTime"", ""b"".""TestDateTimeOffset"", ""b"".""TestTimeSpan"" +FROM ""BuiltInDataTypes"" AS ""b"" +WHERE ""b"".""Id"" = 13"); + } + private void AssertTranslationFailed(Action testCode) => Assert.Contains( CoreStrings.TranslationFailed("").Substring(21),