From a673b89d8821508a62173ae45920d608b60f34b8 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 11 Sep 2022 00:54:10 +0100 Subject: [PATCH 1/2] Use `DbType.Currency` for money parameters Fixes #27831 --- .../Internal/SqlServerTypeMappingSource.cs | 2 +- .../BuiltInDataTypesSqlServerTest.cs | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index 86710ab616f..ae2f0eabd68 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -122,7 +122,7 @@ private readonly DecimalTypeMapping _decimal182 = new SqlServerDecimalTypeMapping("decimal(18, 2)", precision: 18, scale: 2); private readonly DecimalTypeMapping _money - = new SqlServerDecimalTypeMapping("money", storeTypePostfix: StoreTypePostfix.None); + = new SqlServerDecimalTypeMapping("money", DbType.Currency, storeTypePostfix: StoreTypePostfix.None); private readonly TimeSpanTypeMapping _time = new SqlServerTimeSpanTypeMapping("time"); diff --git a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs index 6d0f8e97c6a..f91e9c467d6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs @@ -670,9 +670,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types() @p19='2016-01-02T11:11:12.1234567+00:00' @p20='101.1' (Precision = 4) (Scale = 1) @p21='102.2' (Precision = 4) (Scale = 1) -@p22='81.1' (Precision = 3) (Scale = 1) +@p22='81.1' (DbType = Currency) @p23='103.3' (Precision = 4) (Scale = 1) -@p24='82.2' (Precision = 3) (Scale = 1) +@p24='82.2' (DbType = Currency) @p25='85.5' @p26='83.3' @p27='Value4' (Nullable = false) (Size = 20) @@ -873,9 +873,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_square_b @p14='2016-01-02T11:11:12.1234567+00:00' @p15='101.1' (Precision = 4) (Scale = 1) @p16='102.2' (Precision = 4) (Scale = 1) -@p17='81.1' (Precision = 3) (Scale = 1) +@p17='81.1' (DbType = Currency) @p18='103.3' (Precision = 4) (Scale = 1) -@p19='82.2' (Precision = 3) (Scale = 1) +@p19='82.2' (DbType = Currency) @p20='83.3' @p21='Value4' (Nullable = false) (Size = 20) @p22='Value2' (Nullable = false) (Size = 8000) (DbType = AnsiString) @@ -1041,9 +1041,9 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types() @p19='2016-01-02T11:11:12.9876543+00:00' (Nullable = true) @p20='101.1' (Nullable = true) (Precision = 4) (Scale = 1) @p21='102.2' (Nullable = true) (Precision = 4) (Scale = 1) -@p22='81.1' (Nullable = true) (Precision = 3) (Scale = 1) +@p22='81.1' (Nullable = true) (DbType = Currency) @p23='103.3' (Nullable = true) (Precision = 4) (Scale = 1) -@p24='82.2' (Nullable = true) (Precision = 3) (Scale = 1) +@p24='82.2' (Nullable = true) (DbType = Currency) @p25='85.5' (Nullable = true) @p26='83.3' (Nullable = true) @p27='Value4' (Size = 20) @@ -1235,9 +1235,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null() @p19=NULL (DbType = DateTimeOffset) @p20=NULL (DbType = Decimal) @p21=NULL (DbType = Decimal) -@p22=NULL (DbType = Decimal) +@p22=NULL (DbType = Currency) @p23=NULL (DbType = Decimal) -@p24=NULL (DbType = Decimal) +@p24=NULL (DbType = Currency) @p25=NULL (DbType = Double) @p26=NULL (DbType = Double) @p27=NULL (Size = 20) @@ -1898,9 +1898,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_identity @p18='2016-01-02T11:11:12.7654321+00:00' @p19='101.1' (Precision = 4) (Scale = 1) @p20='102.2' (Precision = 4) (Scale = 1) -@p21='81.1' (Precision = 3) (Scale = 1) +@p21='81.1' (DbType = Currency) @p22='103.3' (Precision = 4) (Scale = 1) -@p23='82.2' (Precision = 3) (Scale = 1) +@p23='82.2' (DbType = Currency) @p24='85.5' @p25='83.3' @p26='Value4' (Nullable = false) (Size = 20) @@ -2095,9 +2095,9 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types_with @p18='2016-01-02T11:11:12.2345678+00:00' (Nullable = true) @p19='101.1' (Nullable = true) (Precision = 4) (Scale = 1) @p20='102.2' (Nullable = true) (Precision = 4) (Scale = 1) -@p21='81.1' (Nullable = true) (Precision = 3) (Scale = 1) +@p21='81.1' (Nullable = true) (DbType = Currency) @p22='103.3' (Nullable = true) (Precision = 4) (Scale = 1) -@p23='82.2' (Nullable = true) (Precision = 3) (Scale = 1) +@p23='82.2' (Nullable = true) (DbType = Currency) @p24='85.5' (Nullable = true) @p25='83.3' (Nullable = true) @p26='Value4' (Size = 20) @@ -2289,9 +2289,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null_w @p18=NULL (DbType = DateTimeOffset) @p19=NULL (DbType = Decimal) @p20=NULL (DbType = Decimal) -@p21=NULL (DbType = Decimal) +@p21=NULL (DbType = Currency) @p22=NULL (DbType = Decimal) -@p23=NULL (DbType = Decimal) +@p23=NULL (DbType = Currency) @p24=NULL (DbType = Double) @p25=NULL (DbType = Double) @p26=NULL (Size = 20) From 3fec527bc240d280c3bcff64ce827fd02a1a3ccd Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 11 Sep 2022 17:03:56 +0100 Subject: [PATCH 2/2] Testing for query strings and various type mapping fixes Issue #27697 --- .../Internal/SqlServerQueryStringFactory.cs | 17 +- .../Internal/SqlServerDateTimeTypeMapping.cs | 30 +- .../Internal/SqlServerDecimalTypeMapping.cs | 27 +- .../Internal/SqlServerTypeMappingSource.cs | 17 +- .../Storage/RelationalTypeMappingTest.cs | 9 +- .../BuiltInDataTypesSqlServerTest.cs | 668 +++++++++++++++++- .../Storage/SqlServerTypeMappingSourceTest.cs | 6 +- .../Storage/SqlServerTypeMappingTest.cs | 17 +- 8 files changed, 733 insertions(+), 58 deletions(-) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs index 19fbbc08126..d4c878fdea7 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs @@ -116,12 +116,25 @@ private static StringBuilder AppendPrecision(this StringBuilder builder, DbParam return builder; } + private static StringBuilder AppendScale(this StringBuilder builder, DbParameter parameter) + { + if (parameter.Scale > 0) + { + builder + .Append('(') + .Append(parameter.Scale.ToString(CultureInfo.InvariantCulture)) + .Append(')'); + } + + return builder; + } + private static StringBuilder AppendPrecisionAndScale(this StringBuilder builder, DbParameter parameter) { if (parameter.Precision > 0 && parameter.Scale > 0) { - builder + return builder .Append('(') .Append(parameter.Precision.ToString(CultureInfo.InvariantCulture)) .Append(',') @@ -161,7 +174,7 @@ public static string CreateTypeName(DbParameter parameter) SqlDbType.SmallMoney => builder.Append("smallmoney"), SqlDbType.Structured => builder.Append("structured"), SqlDbType.Text => builder.Append("text"), - SqlDbType.Time => builder.Append("time").AppendPrecision(parameter), + SqlDbType.Time => builder.Append("time").AppendScale(parameter), SqlDbType.Timestamp => builder.Append("rowversion"), SqlDbType.TinyInt => builder.Append("tinyint"), SqlDbType.Udt => builder.Append(sqlParameter.UdtTypeName), diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs index a683fba1547..a2742e5794d 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs @@ -18,6 +18,8 @@ public class SqlServerDateTimeTypeMapping : DateTimeTypeMapping private const string SmallDateTimeFormatConst = "'{0:yyyy-MM-ddTHH:mm:ss}'"; private const string DateTimeFormatConst = "'{0:yyyy-MM-ddTHH:mm:ss.fff}'"; + private readonly SqlDbType? _sqlDbType; + // Note: this array will be accessed using the precision as an index // so the order of the entries in this array is important private readonly string[] _dateTime2Formats = @@ -41,13 +43,15 @@ public class SqlServerDateTimeTypeMapping : DateTimeTypeMapping public SqlServerDateTimeTypeMapping( string storeType, DbType? dbType = System.Data.DbType.DateTime2, + SqlDbType? sqlDbType = null, StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision) - : base( + : this( new RelationalTypeMappingParameters( new CoreTypeMappingParameters(typeof(DateTime)), storeType, storeTypePostfix, - dbType)) + dbType), + sqlDbType) { } @@ -57,11 +61,21 @@ public SqlServerDateTimeTypeMapping( /// 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 SqlServerDateTimeTypeMapping(RelationalTypeMappingParameters parameters) + protected SqlServerDateTimeTypeMapping(RelationalTypeMappingParameters parameters, SqlDbType? sqlDbType) : base(parameters) { + _sqlDbType = sqlDbType; } + /// + /// 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. + /// + public virtual SqlDbType? SqlType + => _sqlDbType; + /// /// 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 @@ -72,9 +86,13 @@ protected override void ConfigureParameter(DbParameter parameter) { base.ConfigureParameter(parameter); - // Workaround for a SQLClient bug - if (DbType == System.Data.DbType.Date) + if (_sqlDbType != null) + { + ((SqlParameter)parameter).SqlDbType = _sqlDbType.Value; + } + else if (DbType == System.Data.DbType.Date) { + // Workaround for a SQLClient bug ((SqlParameter)parameter).SqlDbType = SqlDbType.Date; } @@ -97,7 +115,7 @@ protected override void ConfigureParameter(DbParameter parameter) /// The parameters for this mapping. /// The newly created mapping. protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new SqlServerDateTimeTypeMapping(parameters); + => new SqlServerDateTimeTypeMapping(parameters, _sqlDbType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs index 5a611348467..eb9cc5141b5 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Data; +using Microsoft.Data.SqlClient; namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; @@ -13,6 +14,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal; /// public class SqlServerDecimalTypeMapping : DecimalTypeMapping { + private readonly SqlDbType? _sqlDbType; + /// /// 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 @@ -24,14 +27,15 @@ public SqlServerDecimalTypeMapping( DbType? dbType = System.Data.DbType.Decimal, int? precision = null, int? scale = null, + SqlDbType? sqlDbType = null, StoreTypePostfix storeTypePostfix = StoreTypePostfix.PrecisionAndScale) - : base( + : this( new RelationalTypeMappingParameters( new CoreTypeMappingParameters(typeof(decimal)), storeType, storeTypePostfix, dbType) - .WithPrecisionAndScale(precision, scale)) + .WithPrecisionAndScale(precision, scale), sqlDbType) { } @@ -41,11 +45,21 @@ public SqlServerDecimalTypeMapping( /// 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 SqlServerDecimalTypeMapping(RelationalTypeMappingParameters parameters) + protected SqlServerDecimalTypeMapping(RelationalTypeMappingParameters parameters, SqlDbType? sqlDbType) : base(parameters) { + _sqlDbType = sqlDbType; } + /// + /// 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. + /// + public virtual SqlDbType? SqlType + => _sqlDbType; + /// /// 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 @@ -53,7 +67,7 @@ protected SqlServerDecimalTypeMapping(RelationalTypeMappingParameters parameters /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new SqlServerDecimalTypeMapping(parameters); + => new SqlServerDecimalTypeMapping(parameters, _sqlDbType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -65,6 +79,11 @@ protected override void ConfigureParameter(DbParameter parameter) { base.ConfigureParameter(parameter); + if (_sqlDbType != null) + { + ((SqlParameter)parameter).SqlDbType = _sqlDbType.Value; + } + if (Size.HasValue && Size.Value != -1) { diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index ae2f0eabd68..0acfb2d5d65 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -88,6 +88,9 @@ private readonly SqlServerByteArrayTypeMapping _fixedLengthBinary private readonly SqlServerDateTimeTypeMapping _date = new("date", DbType.Date); + private readonly SqlServerDateTimeTypeMapping _smallDatetime + = new("smalldatetime", DbType.DateTime, SqlDbType.SmallDateTime); + private readonly SqlServerDateTimeTypeMapping _datetime = new("datetime", DbType.DateTime); @@ -95,7 +98,7 @@ private readonly SqlServerDateTimeTypeMapping _datetime2 = new("datetime2", DbType.DateTime2); private readonly SqlServerDateTimeTypeMapping _datetime2Alias - = new("placeholder", DbType.DateTime2, StoreTypePostfix.None); + = new("placeholder", DbType.DateTime2, null, StoreTypePostfix.None); private readonly DoubleTypeMapping _double = new SqlServerDoubleTypeMapping("float"); @@ -113,7 +116,7 @@ private readonly GuidTypeMapping _uniqueidentifier = new("uniqueidentifier"); private readonly DecimalTypeMapping _decimal - = new SqlServerDecimalTypeMapping("decimal"); + = new SqlServerDecimalTypeMapping("decimal", precision: 18, scale: 0); private readonly DecimalTypeMapping _decimalAlias = new SqlServerDecimalTypeMapping("placeholder", precision: 18, scale: 2, storeTypePostfix: StoreTypePostfix.None); @@ -122,7 +125,11 @@ private readonly DecimalTypeMapping _decimal182 = new SqlServerDecimalTypeMapping("decimal(18, 2)", precision: 18, scale: 2); private readonly DecimalTypeMapping _money - = new SqlServerDecimalTypeMapping("money", DbType.Currency, storeTypePostfix: StoreTypePostfix.None); + = new SqlServerDecimalTypeMapping("money", DbType.Currency, sqlDbType: SqlDbType.Money, storeTypePostfix: StoreTypePostfix.None); + + private readonly DecimalTypeMapping _smallMoney + = new SqlServerDecimalTypeMapping( + "smallmoney", DbType.Currency, sqlDbType: SqlDbType.SmallMoney, storeTypePostfix: StoreTypePostfix.None); private readonly TimeSpanTypeMapping _time = new SqlServerTimeSpanTypeMapping("time"); @@ -214,9 +221,9 @@ public SqlServerTypeMappingSource( { "nvarchar(max)", _variableLengthMaxUnicodeString }, { "real", _real }, { "rowversion", _rowversion }, - { "smalldatetime", _datetime }, + { "smalldatetime", _smallDatetime }, { "smallint", _short }, - { "smallmoney", _money }, + { "smallmoney", _smallMoney }, { "sql_variant", _sqlVariant }, { "text", _textAnsiString }, { "time", _time }, diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs index 9244c1e1f01..ebc95a71f7c 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs @@ -69,6 +69,11 @@ public virtual void Create_and_clone_with_converter(Type mappingType, Type type) null, null); + AssertClone(type, mapping); + } + + protected static RelationalTypeMapping AssertClone(Type type, RelationalTypeMapping mapping) + { var clone = mapping.Clone("", null); Assert.NotSame(mapping, clone); @@ -97,6 +102,8 @@ public virtual void Create_and_clone_with_converter(Type mappingType, Type type) Assert.Same(mapping.ProviderValueComparer, clone.ProviderValueComparer); Assert.Same(typeof(object), clone.ClrType); Assert.Equal(StoreTypePostfix.PrecisionAndScale, clone.StoreTypePostfix); + + return clone; } [ConditionalFact] @@ -230,7 +237,7 @@ protected virtual void UnicodeConversionCloneTest( Assert.Equal(StoreTypePostfix.Size, clone.StoreTypePostfix); } - private class FakeTypeMapping : RelationalTypeMapping + protected class FakeTypeMapping : RelationalTypeMapping { private FakeTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) diff --git a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs index f91e9c467d6..bebd9e797b7 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Globalization; +using Microsoft.Data.SqlClient; // ReSharper disable InconsistentNaming // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local @@ -23,6 +24,601 @@ public BuiltInDataTypesSqlServerTest(BuiltInDataTypesSqlServerFixture fixture, I //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual void Can_query_using_debug_string_BuiltInDataTypes() + { + using var context = CreateContext(); + + context.Add(new BuiltInDataTypes + { + Id = 54, + PartitionId = 1, + TestInt16 = -1234, + TestInt32 = -123456789, + TestInt64 = -1234567890123456789L, + TestDouble = -1.23456789, + TestDecimal = -1234567890.01M, + TestDateTime = Fixture.DefaultDateTime, + TestDateTimeOffset = new DateTimeOffset(new DateTime(), TimeSpan.FromHours(-8.0)), + TestTimeSpan = new TimeSpan(0, 10, 9, 8, 7), + TestSingle = -1.234F, + TestBoolean = true, + TestByte = 255, + TestUnsignedInt16 = 1234, + TestUnsignedInt32 = 1234565789U, + TestUnsignedInt64 = 1234567890123456789UL, + TestCharacter = 'a', + TestSignedByte = -128, + Enum64 = Enum64.SomeValue, + Enum32 = Enum32.SomeValue, + Enum16 = Enum16.SomeValue, + Enum8 = Enum8.SomeValue, + EnumU64 = EnumU64.SomeValue, + EnumU32 = EnumU32.SomeValue, + EnumU16 = EnumU16.SomeValue, + EnumS8 = EnumS8.SomeValue + }); + + context.SaveChanges(); + var set = context.Set(); + + var param1 = (short)-1234; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestInt16 == param1)); + + var param2 = -123456789; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestInt32 == param2)); + + var param3 = -1234567890123456789L; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestInt64 == param3)); + + double? param4 = -1.23456789; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestDouble == param4)); + + var param5 = -1234567890.01M; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestDecimal == param5)); + + var param6 = Fixture.DefaultDateTime; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestDateTime == param6)); + + var param7 = new DateTimeOffset(new DateTime(), TimeSpan.FromHours(-8.0)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestDateTimeOffset == param7)); + + var param8 = new TimeSpan(0, 10, 9, 8, 7); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestTimeSpan == param8)); + + var param9 = -1.234F; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestSingle == param9)); + + var param10 = true; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestBoolean == param10)); + + var param11 = (byte)255; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestByte == param11)); + + var param12 = Enum64.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum64 == param12)); + + var param13 = Enum32.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum32 == param13)); + + var param14 = Enum16.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum16 == param14)); + + var param15 = Enum8.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum8 == param15)); + + var param16 = (ushort)1234; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestUnsignedInt16 == param16)); + + var param17 = 1234565789U; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestUnsignedInt32 == param17)); + + var param18 = 1234567890123456789UL; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestUnsignedInt64 == param18)); + + var param19 = 'a'; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestCharacter == param19)); + + var param20 = (sbyte)-128; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.TestSignedByte == param20)); + + var param21 = EnumU64.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.EnumU64 == param21)); + + var param22 = EnumU32.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.EnumU32 == param22)); + + var param23 = EnumU16.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.EnumU16 == param23)); + + var param24 = EnumS8.SomeValue; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.EnumS8 == param24)); + + var param25 = 1; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum64 == (Enum64)param25)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && (int)e.Enum64 == param25)); + + var param26 = 1; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum32 == (Enum32)param26)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && (int)e.Enum32 == param26)); + + var param27 = 1; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum16 == (Enum16)param27)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && (int)e.Enum16 == param27)); + + var param28 = 1; + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.Enum8 == (Enum8)param28)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && (int)e.Enum8 == param28)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_MaxLengthDataTypes() + { + using var context = CreateContext(); + + var shortString = "Sky"; + var shortBinary = new byte[] { 8, 8, 7, 8, 7 }; + var longString = new string('X', Fixture.LongStringLength); + var longBinary = new byte[Fixture.LongStringLength]; + for (var i = 0; i < longBinary.Length; i++) + { + longBinary[i] = (byte)i; + } + + context.Add(new MaxLengthDataTypes + { + Id = 54, + String3 = shortString, + ByteArray5 = shortBinary, + String9000 = longString, + ByteArray9000 = longBinary + }); + + context.SaveChanges(); + var set = context.Set(); + + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.String3 == shortString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.ByteArray5 == shortBinary)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.String9000 == longString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.ByteArray9000 == longBinary)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_UnicodeDataTypes() + { + using var context = CreateContext(); + + var shortString = Fixture.SupportsUnicodeToAnsiConversion ? "Ϩky" : "sky"; + var longString = Fixture.SupportsUnicodeToAnsiConversion + ? new string('Ϩ', Fixture.LongStringLength) + : new string('s', Fixture.LongStringLength); + + context.Add(new UnicodeDataTypes + { + Id = 54, + StringDefault = shortString, + StringAnsi = shortString, + StringAnsi3 = shortString, + StringAnsi9000 = longString, + StringUnicode = shortString + }); + + context.SaveChanges(); + var set = context.Set(); + + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.StringDefault == shortString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.StringAnsi == shortString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.StringAnsi3 == shortString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.StringUnicode == shortString)); + ExecuteQuerySting(context, 54, set.Where(e => e.Id == 54 && e.StringAnsi9000 == longString)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_MappedDataTypesWithIdentity() + { + using var context = CreateContext(); + + var longAsBigint = 78L; + var shortAsSmallint = (short)79; + var byteAsTinyint = (byte)80; + var uintAsInt = uint.MaxValue; + var ulongAsBigint = ulong.MaxValue; + var uShortAsSmallint = ushort.MaxValue; + var sbyteAsTinyint = sbyte.MinValue; + var boolAsBit = true; + var decimalAsMoney = 81.1m; + var decimalAsSmallmoney = 82.2m; + var doubleAsFloat = 83.3; + var floatAsReal = 84.4f; + var doubleAsDoublePrecision = 85.5; + var dateTimeAsDate = new DateTime(2015, 1, 2, 10, 11, 12); + var dateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(7654321), TimeSpan.Zero); + var dateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(7654321); + var dateTimeAsSmalldatetime = new DateTime(2018, 1, 2, 13, 11, 12); + var dateTimeAsDatetime = new DateTime(2019, 1, 2, 14, 11, 12); + var timeSpanAsTime = new TimeSpan(11, 15, 12); + var stringAsVarcharMax = "C"; + var stringAsCharVaryingMax = "Your"; + var stringAsCharacterVaryingMax = "strong"; + var stringAsNvarcharMax = "don't"; + var stringAsNationalCharVaryingMax = "help"; + var stringAsNationalCharacterVaryingMax = "anyone!"; + var stringAsVarcharMaxUtf8 = "short"; + var stringAsCharVaryingMaxUtf8 = "And now"; + var stringAsCharacterVaryingMaxUtf8 = "this..."; + var stringAsText = "Gumball Rules!"; + var gumballRulesOk = "Gumball Rules OK!"; + var bytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }; + var bytesAsBinaryVaryingMax = new byte[] { 93, 94, 95, 96 }; + var bytesAsImage = new byte[] { 97, 98, 99, 100 }; + var @decimal = 101m; + var decimalAsDec = 102m; + var decimalAsNumeric = 103m; + var guidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"); + var uintAsBigint = uint.MaxValue; + var ulongAsDecimal200 = ulong.MaxValue; + var uShortAsInt = ushort.MaxValue; + var sByteAsSmallint = sbyte.MinValue; + var charAsVarcharMax = 'A'; + var charAsAsCharVaryingMax = 'B'; + var charAsCharacterVaryingMax = 'C'; + var charAsNvarcharMax = 'D'; + var charAsNationalCharVaryingMax = 'E'; + var charAsNationalCharacterVaryingMax = 'F'; + var charAsText = 'G'; + var charAsNtext = 'H'; + var charAsInt = 'I'; + var enumAsNvarchar20 = StringEnumU16.Value4; + var enumAsVarcharMax = StringEnum16.Value2; + var sqlVariantString = "Bang!"; + var sqlVariantInt = 887876; + + var entity = context.Add( + new MappedDataTypesWithIdentity + { + LongAsBigint = longAsBigint, + ShortAsSmallint = shortAsSmallint, + ByteAsTinyint = byteAsTinyint, + UintAsInt = uintAsInt, + UlongAsBigint = ulongAsBigint, + UShortAsSmallint = uShortAsSmallint, + SbyteAsTinyint = sbyteAsTinyint, + BoolAsBit = boolAsBit, + DecimalAsMoney = decimalAsMoney, + DecimalAsSmallmoney = decimalAsSmallmoney, + DoubleAsFloat = doubleAsFloat, + FloatAsReal = floatAsReal, + DoubleAsDoublePrecision = doubleAsDoublePrecision, + DateTimeAsDate = dateTimeAsDate, + DateTimeOffsetAsDatetimeoffset = dateTimeOffsetAsDatetimeoffset, + DateTimeAsDatetime2 = dateTimeAsDatetime2, + DateTimeAsSmalldatetime = dateTimeAsSmalldatetime, + DateTimeAsDatetime = dateTimeAsDatetime, + TimeSpanAsTime = timeSpanAsTime, + StringAsVarcharMax = stringAsVarcharMax, + StringAsCharVaryingMax = stringAsCharVaryingMax, + StringAsCharacterVaryingMax = stringAsCharacterVaryingMax, + StringAsNvarcharMax = stringAsNvarcharMax, + StringAsNationalCharVaryingMax = stringAsNationalCharVaryingMax, + StringAsNationalCharacterVaryingMax = stringAsNationalCharacterVaryingMax, + StringAsVarcharMaxUtf8 = stringAsVarcharMaxUtf8, + StringAsCharVaryingMaxUtf8 = stringAsCharVaryingMaxUtf8, + StringAsCharacterVaryingMaxUtf8 = stringAsCharacterVaryingMaxUtf8, + StringAsText = stringAsText, + StringAsNtext = gumballRulesOk, + BytesAsVarbinaryMax = bytesAsVarbinaryMax, + BytesAsBinaryVaryingMax = bytesAsBinaryVaryingMax, + BytesAsImage = bytesAsImage, + Decimal = @decimal, + DecimalAsDec = decimalAsDec, + DecimalAsNumeric = decimalAsNumeric, + GuidAsUniqueidentifier = guidAsUniqueidentifier, + UintAsBigint = uintAsBigint, + UlongAsDecimal200 = ulongAsDecimal200, + UShortAsInt = uShortAsInt, + SByteAsSmallint = sByteAsSmallint, + CharAsVarcharMax = charAsVarcharMax, + CharAsAsCharVaryingMax = charAsAsCharVaryingMax, + CharAsCharacterVaryingMax = charAsCharacterVaryingMax, + CharAsNvarcharMax = charAsNvarcharMax, + CharAsNationalCharVaryingMax = charAsNationalCharVaryingMax, + CharAsNationalCharacterVaryingMax = charAsNationalCharacterVaryingMax, + CharAsText = charAsText, + CharAsNtext = charAsNtext, + CharAsInt = charAsInt, + EnumAsNvarchar20 = enumAsNvarchar20, + EnumAsVarcharMax = enumAsVarcharMax, + SqlVariantString = sqlVariantString, + SqlVariantInt = sqlVariantInt + }).Entity; + + context.SaveChanges(); + var id = entity.Id; + var set = context.Set(); + + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.LongAsBigint == longAsBigint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.ShortAsSmallint == shortAsSmallint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.ByteAsTinyint == byteAsTinyint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UintAsInt == uintAsInt)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UlongAsBigint == ulongAsBigint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UShortAsSmallint == uShortAsSmallint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.SbyteAsTinyint == sbyteAsTinyint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BoolAsBit == boolAsBit)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsMoney == decimalAsMoney)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsSmallmoney == decimalAsSmallmoney)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DoubleAsFloat == doubleAsFloat)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.FloatAsReal == floatAsReal)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DoubleAsDoublePrecision == doubleAsDoublePrecision)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeAsDate == dateTimeAsDate)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeOffsetAsDatetimeoffset == dateTimeOffsetAsDatetimeoffset)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeAsDatetime2 == dateTimeAsDatetime2)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeAsSmalldatetime == dateTimeAsSmalldatetime)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeAsDatetime == dateTimeAsDatetime)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.TimeSpanAsTime == timeSpanAsTime)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsVarcharMax == stringAsVarcharMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharVaryingMax == stringAsCharVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacterVaryingMax == stringAsCharacterVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNvarcharMax == stringAsNvarcharMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNationalCharVaryingMax == stringAsNationalCharVaryingMax)); + ExecuteQuerySting( + context, id, set.Where(e => e.Id == id && e.StringAsNationalCharacterVaryingMax == stringAsNationalCharacterVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsVarcharMaxUtf8 == stringAsVarcharMaxUtf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharVaryingMaxUtf8 == stringAsCharVaryingMaxUtf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacterVaryingMaxUtf8 == stringAsCharacterVaryingMaxUtf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsVarbinaryMax == bytesAsVarbinaryMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsBinaryVaryingMax == bytesAsBinaryVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.Decimal == @decimal)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsDec == decimalAsDec)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsNumeric == decimalAsNumeric)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.GuidAsUniqueidentifier == guidAsUniqueidentifier)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UintAsBigint == uintAsBigint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UlongAsDecimal200 == ulongAsDecimal200)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.UShortAsInt == uShortAsInt)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.SByteAsSmallint == sByteAsSmallint)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsVarcharMax == charAsVarcharMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsAsCharVaryingMax == charAsAsCharVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsCharacterVaryingMax == charAsCharacterVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNvarcharMax == charAsNvarcharMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNationalCharVaryingMax == charAsNationalCharVaryingMax)); + ExecuteQuerySting( + context, id, set.Where(e => e.Id == id && e.CharAsNationalCharacterVaryingMax == charAsNationalCharacterVaryingMax)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsInt == charAsInt)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.EnumAsNvarchar20 == enumAsNvarchar20)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.EnumAsVarcharMax == enumAsVarcharMax)); + + // The text, ntext, and image data types are invalid for local variables. + Assert.Contains( + "text", + Assert.Throws( + () => ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsText == stringAsText))).Message); + + Assert.Contains( + "ntext", + Assert.Throws( + () => ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNtext == gumballRulesOk))).Message); + + Assert.Contains( + "image", + Assert.Throws( + () => ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsImage == bytesAsImage))).Message); + + Assert.Contains( + "text", + Assert.Throws( + () => ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsText == charAsText))).Message); + + Assert.Contains( + "ntext", + Assert.Throws( + () => ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNtext == charAsNtext))).Message); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_MappedSizedDataTypes() + { + using var context = CreateContext(); + + var stringAsChar3 = "Wor"; + var stringAsCharacter3 = "Lon"; + var stringAsVarchar3 = "Tha"; + var stringAsCharVarying3 = "Thr"; + var stringAsCharacterVarying3 = "Let"; + var stringAsNchar3 = "Won"; + var stringAsNationalCharacter3 = "Squ"; + var stringAsNvarchar3 = "Int"; + var stringAsNationalCharVarying3 = "The"; + var stringAsNationalCharacterVarying3 = "Col"; + var stringAsChar3Utf8 = "Wha"; + var stringAsCharacter3Utf8 = "doe"; + var stringAsVarchar3Utf8 = "the"; + var stringAsCharVarying3Utf8 = "tex"; + var stringAsCharacterVarying3Utf8 = "men"; + var bytesAsBinary3 = new byte[] { 10, 11, 12 }; + var bytesAsVarbinary3 = new byte[] { 11, 12, 13 }; + var bytesAsBinaryVarying3 = new byte[] { 12, 13, 14 }; + var charAsVarchar3 = 'A'; + var charAsAsCharVarying3 = 'B'; + var charAsCharacterVarying3 = 'C'; + var charAsNvarchar3 = 'D'; + var charAsNationalCharVarying3 = 'E'; + var charAsNationalCharacterVarying3 = 'F'; + + var entity = context.Add( + new MappedSizedDataTypes + { + Id = 54, + StringAsChar3 = stringAsChar3, + StringAsCharacter3 = stringAsCharacter3, + StringAsVarchar3 = stringAsVarchar3, + StringAsCharVarying3 = stringAsCharVarying3, + StringAsCharacterVarying3 = stringAsCharacterVarying3, + StringAsNchar3 = stringAsNchar3, + StringAsNationalCharacter3 = stringAsNationalCharacter3, + StringAsNvarchar3 = stringAsNvarchar3, + StringAsNationalCharVarying3 = stringAsNationalCharVarying3, + StringAsNationalCharacterVarying3 = stringAsNationalCharacterVarying3, + StringAsChar3Utf8 = stringAsChar3Utf8, + StringAsCharacter3Utf8 = stringAsCharacter3Utf8, + StringAsVarchar3Utf8 = stringAsVarchar3Utf8, + StringAsCharVarying3Utf8 = stringAsCharVarying3Utf8, + StringAsCharacterVarying3Utf8 = stringAsCharacterVarying3Utf8, + BytesAsBinary3 = bytesAsBinary3, + BytesAsVarbinary3 = bytesAsVarbinary3, + BytesAsBinaryVarying3 = bytesAsBinaryVarying3, + CharAsVarchar3 = charAsVarchar3, + CharAsAsCharVarying3 = charAsAsCharVarying3, + CharAsCharacterVarying3 = charAsCharacterVarying3, + CharAsNvarchar3 = charAsNvarchar3, + CharAsNationalCharVarying3 = charAsNationalCharVarying3, + CharAsNationalCharacterVarying3 = charAsNationalCharacterVarying3 + }).Entity; + + context.SaveChanges(); + var id = entity.Id; + var set = context.Set(); + + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsChar3 == stringAsChar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacter3 == stringAsCharacter3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsVarchar3 == stringAsVarchar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharVarying3 == stringAsCharVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacterVarying3 == stringAsCharacterVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNchar3 == stringAsNchar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNationalCharacter3 == stringAsNationalCharacter3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNvarchar3 == stringAsNvarchar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsNationalCharVarying3 == stringAsNationalCharVarying3)); + ExecuteQuerySting( + context, id, set.Where(e => e.Id == id && e.StringAsNationalCharacterVarying3 == stringAsNationalCharacterVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsChar3Utf8 == stringAsChar3Utf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacter3Utf8 == stringAsCharacter3Utf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsVarchar3Utf8 == stringAsVarchar3Utf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharVarying3Utf8 == stringAsCharVarying3Utf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.StringAsCharacterVarying3Utf8 == stringAsCharacterVarying3Utf8)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsBinary3 == bytesAsBinary3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsVarbinary3 == bytesAsVarbinary3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.BytesAsBinaryVarying3 == bytesAsBinaryVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsVarchar3 == charAsVarchar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsAsCharVarying3 == charAsAsCharVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsCharacterVarying3 == charAsCharacterVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNvarchar3 == charAsNvarchar3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNationalCharVarying3 == charAsNationalCharVarying3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.CharAsNationalCharacterVarying3 == charAsNationalCharacterVarying3)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_MappedScaledDataTypes() + { + using var context = CreateContext(); + + var floatAsFloat3 = 83.3f; + var floatAsDoublePrecision3 = 85.5f; + var floatAsFloat25 = 83.33f; + var floatAsDoublePrecision25 = 85.55f; + var dateTimeOffsetAsDatetimeoffset3 = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 765), TimeSpan.Zero); + var dateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 321); + var decimalAsDecimal3 = 101m; + var decimalAsDec3 = 102m; + var decimalAsNumeric3 = 103m; + var timeSpanAsTime3 = TimeSpan.Parse("12:34:56.7890123", CultureInfo.InvariantCulture); + var entity = context.Add( + new MappedScaledDataTypes + { + Id = 54, + FloatAsFloat3 = floatAsFloat3, + FloatAsDoublePrecision3 = floatAsDoublePrecision3, + FloatAsFloat25 = floatAsFloat25, + FloatAsDoublePrecision25 = floatAsDoublePrecision25, + DateTimeOffsetAsDatetimeoffset3 = dateTimeOffsetAsDatetimeoffset3, + DateTimeAsDatetime23 = dateTimeAsDatetime23, + DecimalAsDecimal3 = decimalAsDecimal3, + DecimalAsDec3 = decimalAsDec3, + DecimalAsNumeric3 = decimalAsNumeric3, + TimeSpanAsTime3 = timeSpanAsTime3 + }).Entity; + + context.SaveChanges(); + var id = entity.Id; + var set = context.Set(); + + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.FloatAsFloat3 == floatAsFloat3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.FloatAsDoublePrecision3 == floatAsDoublePrecision3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.FloatAsFloat25 == floatAsFloat25)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.FloatAsDoublePrecision25 == floatAsDoublePrecision25)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeOffsetAsDatetimeoffset3 == dateTimeOffsetAsDatetimeoffset3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DateTimeAsDatetime23 == dateTimeAsDatetime23)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsDecimal3 == decimalAsDecimal3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsDec3 == decimalAsDec3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsNumeric3 == decimalAsNumeric3)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.TimeSpanAsTime3 == timeSpanAsTime3)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_MappedPrecisionAndScaledDataTypes() + { + using var context = CreateContext(); + + var decimalAsDecimal52 = 101.1m; + var decimalAsDec52 = 102.2m; + var decimalAsNumeric52 = 103.3m; + var entity = context.Add( + new MappedPrecisionAndScaledDataTypes + { + Id = 54, + DecimalAsDecimal52 = decimalAsDecimal52, + DecimalAsDec52 = decimalAsDec52, + DecimalAsNumeric52 = decimalAsNumeric52 + }).Entity; + + context.SaveChanges(); + var id = entity.Id; + var set = context.Set(); + + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsDecimal52 == decimalAsDecimal52)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsDec52 == decimalAsDec52)); + ExecuteQuerySting(context, id, set.Where(e => e.Id == id && e.DecimalAsNumeric52 == decimalAsNumeric52)); + } + + [ConditionalFact] + public virtual void Can_query_using_debug_string_for_non_integer_values() + { + using var context = CreateContext(); + + var accumulator0 = 1L; + var accumulator1 = 1L; + for (var i = 0; i < 100; i++) + { + var temp = accumulator1; + accumulator1 += accumulator0; + accumulator0 = temp; + + var @double = ((double)accumulator1) / accumulator0; + var @float = ((float)accumulator1) / accumulator0; + + var entity = context.Add( + new MappedNullableDataTypesWithIdentity { DoubleAsFloat = @double, FloatAsReal = @float }).Entity; + + context.SaveChanges(); + var id = entity.Id; + + ExecuteQuerySting( + context, id, context.Set().Where( + e => e.Id == id && e.DoubleAsFloat == @double && e.FloatAsReal == @float)); + } + } + + private void ExecuteQuerySting(DbContext context, int expectedId, IQueryable queryable) + { + var queryString = queryable.ToQueryString(); + using var command = context.Database.GetDbConnection().CreateCommand(); + command.CommandText = queryString; + using var reader = command.ExecuteReader(); + + Assert.True(reader.HasRows); + Assert.True(reader.Read()); + Assert.Equal(expectedId, reader.GetFieldValue(reader.GetOrdinal("Id"))); + Assert.False(reader.Read()); + reader.Close(); + } + [ConditionalFact] public void Sql_translation_uses_type_mapper_when_constant() { @@ -668,10 +1264,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types() @p17='2017-01-02T12:11:12.1234567' @p18='2018-01-02T13:11:12.0000000' (DbType = DateTime) @p19='2016-01-02T11:11:12.1234567+00:00' -@p20='101.1' (Precision = 4) (Scale = 1) -@p21='102.2' (Precision = 4) (Scale = 1) +@p20='101' (Precision = 18) +@p21='102' (Precision = 18) @p22='81.1' (DbType = Currency) -@p23='103.3' (Precision = 4) (Scale = 1) +@p23='103' (Precision = 18) @p24='82.2' (DbType = Currency) @p25='85.5' @p26='83.3' @@ -820,9 +1416,9 @@ private static MappedDataTypes CreateMappedDataTypes(int id) BytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }, BytesAsBinaryVaryingMax = new byte[] { 93, 94, 95, 96 }, BytesAsImage = new byte[] { 97, 98, 99, 100 }, - Decimal = 101.1m, - DecimalAsDec = 102.2m, - DecimalAsNumeric = 103.3m, + Decimal = 101m, + DecimalAsDec = 102m, + DecimalAsNumeric = 103m, GuidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"), UintAsBigint = uint.MaxValue, UlongAsDecimal200 = ulong.MaxValue, @@ -871,10 +1467,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_square_b @p12='2017-01-02T12:11:12.1234567' @p13='2018-01-02T13:11:12.0000000' (DbType = DateTime) @p14='2016-01-02T11:11:12.1234567+00:00' -@p15='101.1' (Precision = 4) (Scale = 1) -@p16='102.2' (Precision = 4) (Scale = 1) +@p15='101' (Precision = 18) +@p16='102' (Precision = 18) @p17='81.1' (DbType = Currency) -@p18='103.3' (Precision = 4) (Scale = 1) +@p18='103' (Precision = 18) @p19='82.2' (DbType = Currency) @p20='83.3' @p21='Value4' (Nullable = false) (Size = 20) @@ -988,9 +1584,9 @@ private static MappedSquareDataTypes CreateMappedSquareDataTypes(int id) StringAsNtext = "Gumball Rules OK!", BytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }, BytesAsImage = new byte[] { 97, 98, 99, 100 }, - Decimal = 101.1m, - DecimalAsDec = 102.2m, - DecimalAsNumeric = 103.3m, + Decimal = 101m, + DecimalAsDec = 102m, + DecimalAsNumeric = 103m, GuidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"), UintAsBigint = uint.MaxValue, UlongAsDecimal200 = ulong.MaxValue, @@ -1039,10 +1635,10 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types() @p17='2017-01-02T12:11:12.9876543' (Nullable = true) @p18='2018-01-02T13:11:12.0000000' (Nullable = true) (DbType = DateTime) @p19='2016-01-02T11:11:12.9876543+00:00' (Nullable = true) -@p20='101.1' (Nullable = true) (Precision = 4) (Scale = 1) -@p21='102.2' (Nullable = true) (Precision = 4) (Scale = 1) +@p20='101' (Nullable = true) (Precision = 18) +@p21='102' (Nullable = true) (Precision = 18) @p22='81.1' (Nullable = true) (DbType = Currency) -@p23='103.3' (Nullable = true) (Precision = 4) (Scale = 1) +@p23='103' (Nullable = true) (Precision = 18) @p24='82.2' (Nullable = true) (DbType = Currency) @p25='85.5' (Nullable = true) @p26='83.3' (Nullable = true) @@ -1178,9 +1774,9 @@ private static MappedNullableDataTypes CreateMappedNullableDataTypes(int id) BytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }, BytesAsBinaryVaryingMax = new byte[] { 93, 94, 95, 96 }, BytesAsImage = new byte[] { 97, 98, 99, 100 }, - Decimal = 101.1m, - DecimalAsDec = 102.2m, - DecimalAsNumeric = 103.3m, + Decimal = 101m, + DecimalAsDec = 102m, + DecimalAsNumeric = 103m, GuidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"), UintAsBigint = uint.MaxValue, UlongAsDecimal200 = ulong.MaxValue, @@ -1233,10 +1829,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null() @p17=NULL (DbType = DateTime2) @p18=NULL (DbType = DateTime) @p19=NULL (DbType = DateTimeOffset) -@p20=NULL (DbType = Decimal) -@p21=NULL (DbType = Decimal) +@p20=NULL (Precision = 18) (DbType = Decimal) +@p21=NULL (Precision = 18) (DbType = Decimal) @p22=NULL (DbType = Currency) -@p23=NULL (DbType = Decimal) +@p23=NULL (Precision = 18) (DbType = Decimal) @p24=NULL (DbType = Currency) @p25=NULL (DbType = Double) @p26=NULL (DbType = Double) @@ -1896,10 +2492,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_identity @p16='2017-01-02T12:11:12.7654321' @p17='2018-01-02T13:11:12.0000000' (DbType = DateTime) @p18='2016-01-02T11:11:12.7654321+00:00' -@p19='101.1' (Precision = 4) (Scale = 1) -@p20='102.2' (Precision = 4) (Scale = 1) +@p19='101' (Precision = 18) +@p20='102' (Precision = 18) @p21='81.1' (DbType = Currency) -@p22='103.3' (Precision = 4) (Scale = 1) +@p22='103' (Precision = 18) @p23='82.2' (DbType = Currency) @p24='85.5' @p25='83.3' @@ -2039,9 +2635,9 @@ private static MappedDataTypesWithIdentity CreateMappedDataTypesWithIdentity(int BytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }, BytesAsBinaryVaryingMax = new byte[] { 93, 94, 95, 96 }, BytesAsImage = new byte[] { 97, 98, 99, 100 }, - Decimal = 101.1m, - DecimalAsDec = 102.2m, - DecimalAsNumeric = 103.3m, + Decimal = 101m, + DecimalAsDec = 102m, + DecimalAsNumeric = 103m, GuidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"), UintAsBigint = uint.MaxValue, UlongAsDecimal200 = ulong.MaxValue, @@ -2093,10 +2689,10 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types_with @p16='2017-01-02T12:11:12.2345678' (Nullable = true) @p17='2018-01-02T13:11:12.0000000' (Nullable = true) (DbType = DateTime) @p18='2016-01-02T11:11:12.2345678+00:00' (Nullable = true) -@p19='101.1' (Nullable = true) (Precision = 4) (Scale = 1) -@p20='102.2' (Nullable = true) (Precision = 4) (Scale = 1) +@p19='101' (Nullable = true) (Precision = 18) +@p20='102' (Nullable = true) (Precision = 18) @p21='81.1' (Nullable = true) (DbType = Currency) -@p22='103.3' (Nullable = true) (Precision = 4) (Scale = 1) +@p22='103' (Nullable = true) (Precision = 18) @p23='82.2' (Nullable = true) (DbType = Currency) @p24='85.5' (Nullable = true) @p25='83.3' (Nullable = true) @@ -2233,9 +2829,9 @@ private static MappedNullableDataTypesWithIdentity CreateMappedNullableDataTypes BytesAsVarbinaryMax = new byte[] { 89, 90, 91, 92 }, BytesAsVaryingMax = new byte[] { 93, 94, 95, 96 }, BytesAsImage = new byte[] { 97, 98, 99, 100 }, - Decimal = 101.1m, - DecimalAsDec = 102.2m, - DecimalAsNumeric = 103.3m, + Decimal = 101m, + DecimalAsDec = 102m, + DecimalAsNumeric = 103m, GuidAsUniqueidentifier = new Guid("A8F9F951-145F-4545-AC60-B92FF57ADA47"), UintAsBigint = uint.MaxValue, UlongAsDecimal200 = ulong.MaxValue, @@ -2287,10 +2883,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null_w @p16=NULL (DbType = DateTime2) @p17=NULL (DbType = DateTime) @p18=NULL (DbType = DateTimeOffset) -@p19=NULL (DbType = Decimal) -@p20=NULL (DbType = Decimal) +@p19=NULL (Precision = 18) (DbType = Decimal) +@p20=NULL (Precision = 18) (DbType = Decimal) @p21=NULL (DbType = Currency) -@p22=NULL (DbType = Decimal) +@p22=NULL (Precision = 18) (DbType = Decimal) @p23=NULL (DbType = Currency) @p24=NULL (DbType = Double) @p25=NULL (DbType = Double) diff --git a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs index 4a80bf5becc..06be8f24a22 100644 --- a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs +++ b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs @@ -993,8 +993,8 @@ public void Throws_for_unrecognized_property_types() [InlineData("datetime", typeof(DateTime), null, false, false)] [InlineData("datetime2", typeof(DateTime), null, false, false)] [InlineData("datetimeoffset", typeof(DateTimeOffset), null, false, false)] - [InlineData("dec", typeof(decimal), null, false, false)] - [InlineData("decimal", typeof(decimal), null, false, false)] + [InlineData("dec", typeof(decimal), null, false, false, "dec(18,0)")] + [InlineData("decimal", typeof(decimal), null, false, false, "decimal(18,0)")] [InlineData("float", typeof(double), null, false, false)] // This is correct. SQL Server 'float' type maps to C# double [InlineData("float(10)", typeof(double), null, false, false)] [InlineData("image", typeof(byte[]), null, false, false)] @@ -1007,7 +1007,7 @@ public void Throws_for_unrecognized_property_types() [InlineData("national character(333)", typeof(string), 333, true, true)] [InlineData("nchar(333)", typeof(string), 333, true, true)] [InlineData("ntext", typeof(string), null, true, false)] - [InlineData("numeric", typeof(decimal), null, false, false)] + [InlineData("numeric", typeof(decimal), null, false, false, "numeric(18,0)")] [InlineData("nvarchar(333)", typeof(string), 333, true, false)] [InlineData("nvarchar(max)", typeof(string), null, true, false)] [InlineData("real", typeof(float), null, false, false)] diff --git a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs index 8995c2a5a97..74d88ee68a4 100644 --- a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs +++ b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingTest.cs @@ -79,13 +79,28 @@ protected override DbCommand CreateTestCommand() [ConditionalTheory] [InlineData(typeof(SqlServerDateTimeOffsetTypeMapping), typeof(DateTimeOffset))] - [InlineData(typeof(SqlServerDateTimeTypeMapping), typeof(DateTime))] [InlineData(typeof(SqlServerDoubleTypeMapping), typeof(double))] [InlineData(typeof(SqlServerFloatTypeMapping), typeof(float))] [InlineData(typeof(SqlServerTimeSpanTypeMapping), typeof(TimeSpan))] public override void Create_and_clone_with_converter(Type mappingType, Type type) => base.Create_and_clone_with_converter(mappingType, type); + [ConditionalFact] + public void Create_and_clone_SQL_Server_DateTime_mappings_with_converter() + { + var mapping = (RelationalTypeMapping)Activator.CreateInstance( + typeof(SqlServerDateTimeTypeMapping), + BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, + null, + new[] { FakeTypeMapping.CreateParameters(typeof(SqlServerDateTimeTypeMapping)), SqlDbType.SmallDateTime }, + null, + null); + + var clone = AssertClone(typeof(SqlServerDateTimeTypeMapping), mapping); + + Assert.Equal(SqlDbType.SmallDateTime, ((SqlServerDateTimeTypeMapping)clone).SqlType); + } + [ConditionalFact] public virtual void Create_and_clone_SQL_Server_sized_mappings_with_converter() => ConversionCloneTest(