diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteByteArrayTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteByteArrayTypeMapping.cs index e2bccc551a7..0c223a56263 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteByteArrayTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteByteArrayTypeMapping.cs @@ -35,20 +35,28 @@ public SqliteByteArrayTypeMapping(string storeType, DbType? dbType = System.Data typeof(byte[]), jsonValueReaderWriter: SqliteJsonByteArrayReaderWriter.Instance), storeType, - dbType: dbType)) + dbType: dbType), + false) { } + private SqliteByteArrayTypeMapping( + RelationalTypeMappingParameters parameters, + bool isJsonColumn) + : base(parameters) + { + _isJsonColumn = isJsonColumn; + } + + private readonly bool _isJsonColumn; + /// /// 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 SqliteByteArrayTypeMapping(RelationalTypeMappingParameters parameters) - : base(parameters) - { - } + /// /// Creates a copy of this mapping. @@ -56,5 +64,21 @@ protected SqliteByteArrayTypeMapping(RelationalTypeMappingParameters parameters) /// The parameters for this mapping. /// The newly created mapping. protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new SqliteByteArrayTypeMapping(parameters); + => new SqliteByteArrayTypeMapping(parameters, _isJsonColumn); + + internal SqliteByteArrayTypeMapping WithJsonColumn() + => new(Parameters, true); + + /// + /// Configures the parameter, setting the to + /// when the mapping is for a JSON column. + /// + /// The parameter to be configured. + protected override void ConfigureParameter(DbParameter parameter) + { + if (_isJsonColumn && parameter is Data.Sqlite.SqliteParameter sqliteParameter) + { + sqliteParameter.SqliteType = Data.Sqlite.SqliteType.Text; + } + } } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs index f10b7d6d398..993dafbd51f 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Data; +using Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal; using Microsoft.EntityFrameworkCore.Storage.Json; namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal; @@ -33,7 +34,7 @@ public SqliteTimeOnlyTypeMapping( DbType? dbType = System.Data.DbType.Time) : base( new RelationalTypeMappingParameters( - new CoreTypeMappingParameters(typeof(TimeOnly), jsonValueReaderWriter: JsonTimeOnlyReaderWriter.Instance), + new CoreTypeMappingParameters(typeof(TimeOnly), jsonValueReaderWriter: SqliteJsonTimeOnlyReaderWriter.Instance), storeType, dbType: dbType)) { diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index d28b9373970..e971c45d499 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -131,6 +131,28 @@ public static bool IsSpatialiteType(string columnType) : mapping; } + /// + /// 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. + /// + /// + /// Finds the type mapping for a given property. This method is overridden to special-case byte[] + /// properties inside JSON columns, returning a mapping that works with Base64-encoded strings. + /// + /// The property for which mapping is to be found. + /// The type mapping, or if none was found. + public override RelationalTypeMapping? FindMapping(IProperty property) + { + var mapping = base.FindMapping(property); + if (mapping is SqliteByteArrayTypeMapping byteArrayMapping && property.DeclaringType.IsMappedToJson()) + { + return byteArrayMapping.WithJsonColumn(); + } + return mapping; + } + private RelationalTypeMapping? FindRawMapping(RelationalTypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType; diff --git a/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonTimeOnlyReaderWriter.cs b/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonTimeOnlyReaderWriter.cs new file mode 100644 index 00000000000..c163d4017a3 --- /dev/null +++ b/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonTimeOnlyReaderWriter.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Text.Json; +using Microsoft.EntityFrameworkCore.Storage.Json; + +namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal; + +/// +/// The Sqlite-specific JsonValueReaderWrite for byte[]. Generates the SQLite representation (e.g. X'0102') rather than base64, in order +/// to match our SQLite non-JSON representation. +/// +/// +/// 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 sealed class SqliteJsonTimeOnlyReaderWriter : JsonValueReaderWriter +{ + private static readonly PropertyInfo InstanceProperty = typeof(SqliteJsonTimeOnlyReaderWriter).GetProperty(nameof(Instance))!; + + /// + /// 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 static SqliteJsonTimeOnlyReaderWriter Instance { get; } = new(); + + private SqliteJsonTimeOnlyReaderWriter() + { + } + + /// + /// 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 override TimeOnly FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null) + => TimeOnly.Parse(manager.CurrentReader.GetString()!); + + /// + /// 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 override void ToJsonTyped(Utf8JsonWriter writer, TimeOnly value) + => writer.WriteStringValue(value.Ticks % TimeSpan.TicksPerSecond == 0 ? string.Format(CultureInfo.InvariantCulture, @"{0:HH\:mm\:ss}", value) + : value.ToString("o")); + + /// + public override Expression ConstructorExpression + => Expression.Property(null, InstanceProperty); +} diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs b/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs index 35fca9e4a44..549f8a18820 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs @@ -61,8 +61,17 @@ public virtual void Bind() } else if (type == typeof(byte[])) { + // In Sqlite json columns parameter binding for byte[] resulted in a blob being bound to whereas the column is a text column thats why this conversion is needed. var value1 = (byte[])value; - BindBlob(value1); + if (sqliteType == SqliteType.Text) + { + var value = ToHexString(value1); + BindText(value); + } + else + { + BindBlob(value1); + } } else if (type == typeof(char)) { @@ -313,4 +322,24 @@ private static double GetTotalDays(int hour, int minute, int second, int millise return iJD / 86400000.0; } + + private static string ToHexString(byte[] bytes) + { + + char[] hexChars = new char[bytes.Length * 2]; + + for (int i = 0; i < bytes.Length; i++) + { + byte b = bytes[i]; + + int highNibble = (b >> 4); + int lowNibble = (b & 0x0F); + + hexChars[i * 2] = (char)(highNibble < 10 ? highNibble + '0' : highNibble + 'A' - 10); + hexChars[i * 2 + 1] = (char)(lowNibble < 10 ? lowNibble + '0' : lowNibble + 'A' - 10); + } + + return new string(hexChars); + } + } diff --git a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs index 08a6e784d2b..c0ba0231233 100644 --- a/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/JsonTypesSqliteTest.cs @@ -184,6 +184,51 @@ public override Task Can_read_write_collection_of_nullable_GUID_JSON_values(stri => base.Can_read_write_collection_of_nullable_GUID_JSON_values( """{"Prop":["00000000-0000-0000-0000-000000000000",null,"8C44242F-8E3F-4A20-8BE8-98C7C1AADEBD","FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"]}"""); + public override Task Can_read_write_TimeOnly_JSON_values(string value, string json) + => base.Can_read_write_TimeOnly_JSON_values( + value, value switch + { + "00:00:00.0000000" => """{"Prop":"00:00:00"}""", + "23:59:59.9999999" => """{"Prop":"23:59:59.9999999"}""", + "11:05:12.3456789" => """{"Prop":"11:05:12.3456789"}""", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }); + + public override Task Can_read_write_nullable_TimeOnly_JSON_values(string? value, string json) + => base.Can_read_write_nullable_TimeOnly_JSON_values( + value, value switch + { + "00:00:00.0000000" => """{"Prop":"00:00:00"}""", + "23:59:59.9999999" => """{"Prop":"23:59:59.9999999"}""", + "11:05:12.3456789" => """{"Prop":"11:05:12.3456789"}""", + null => """{"Prop":null}""", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }); + + public override Task Can_read_write_collection_of_TimeOnly_JSON_values() + => Can_read_and_write_JSON_value>( + nameof(TimeOnlyCollectionType.TimeOnly), + [ + TimeOnly.MinValue, + new TimeOnly(11, 5, 2, 3, 4), + TimeOnly.MaxValue + ], + """{"Prop":["00:00:00","11:05:02.0030040","23:59:59.9999999"]}""", + mappedCollection: true, + new List()); + + public override Task Can_read_write_collection_of_nullable_TimeOnly_JSON_values() + => Can_read_and_write_JSON_value>( + nameof(NullableTimeOnlyCollectionType.TimeOnly), + [ + null, + TimeOnly.MinValue, + new TimeOnly(11, 5, 2, 3, 4), + TimeOnly.MaxValue + ], + """{"Prop":[null,"00:00:00","11:05:02.0030040","23:59:59.9999999"]}""", + mappedCollection: true); + public override Task Can_read_write_ulong_enum_JSON_values(EnumU64 value, string json) => Can_read_and_write_JSON_value(nameof(EnumU64Type.EnumU64), value, json); diff --git a/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel/ManyTypesEntityType.cs b/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel/ManyTypesEntityType.cs index 92d0ef51ea3..a954d48cebc 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel/ManyTypesEntityType.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel/ManyTypesEntityType.cs @@ -12219,9 +12219,9 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas int (string v) => ((object)v).GetHashCode(), string (string v) => v), converter: new CollectionToJsonStringConverter(new JsonCollectionOfNullableStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance)), + SqliteJsonTimeOnlyReaderWriter.Instance)), jsonValueReaderWriter: new JsonCollectionOfNullableStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance), + SqliteJsonTimeOnlyReaderWriter.Instance), elementMapping: SqliteTimeOnlyTypeMapping.Default); var nullableTimeOnlyArrayElementType = nullableTimeOnlyArray.SetElementType(typeof(TimeOnly?), nullable: true); @@ -13956,7 +13956,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas TimeOnly (string v) => TimeOnly.Parse(v, CultureInfo.InvariantCulture, DateTimeStyles.None), string (TimeOnly v) => (v.Ticks % 10000000L == 0L ? string.Format(CultureInfo.InvariantCulture, "{0:HH\\:mm\\:ss}", ((object)(v))) : v.ToString("o"))), jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonTimeOnlyReaderWriter.Instance, + SqliteJsonTimeOnlyReaderWriter.Instance, new ValueConverter( TimeOnly (string v) => TimeOnly.Parse(v, CultureInfo.InvariantCulture, DateTimeStyles.None), string (TimeOnly v) => (v.Ticks % 10000000L == 0L ? string.Format(CultureInfo.InvariantCulture, "{0:HH\\:mm\\:ss}", ((object)(v))) : v.ToString("o"))))); @@ -14151,9 +14151,9 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas int (string v) => ((object)v).GetHashCode(), string (string v) => v), converter: new CollectionToJsonStringConverter(new JsonCollectionOfStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance)), + SqliteJsonTimeOnlyReaderWriter.Instance)), jsonValueReaderWriter: new JsonCollectionOfStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance), + SqliteJsonTimeOnlyReaderWriter.Instance), elementMapping: SqliteTimeOnlyTypeMapping.Default); var timeOnlyArrayElementType = timeOnlyArray.SetElementType(typeof(TimeOnly)); timeOnlyArrayElementType.TypeMapping = timeOnlyArray.TypeMapping.ElementTypeMapping; diff --git a/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel_with_JSON_columns/ManyTypesEntityType.cs b/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel_with_JSON_columns/ManyTypesEntityType.cs index 92d0ef51ea3..a954d48cebc 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel_with_JSON_columns/ManyTypesEntityType.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Scaffolding/Baselines/BigModel_with_JSON_columns/ManyTypesEntityType.cs @@ -12219,9 +12219,9 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas int (string v) => ((object)v).GetHashCode(), string (string v) => v), converter: new CollectionToJsonStringConverter(new JsonCollectionOfNullableStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance)), + SqliteJsonTimeOnlyReaderWriter.Instance)), jsonValueReaderWriter: new JsonCollectionOfNullableStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance), + SqliteJsonTimeOnlyReaderWriter.Instance), elementMapping: SqliteTimeOnlyTypeMapping.Default); var nullableTimeOnlyArrayElementType = nullableTimeOnlyArray.SetElementType(typeof(TimeOnly?), nullable: true); @@ -13956,7 +13956,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas TimeOnly (string v) => TimeOnly.Parse(v, CultureInfo.InvariantCulture, DateTimeStyles.None), string (TimeOnly v) => (v.Ticks % 10000000L == 0L ? string.Format(CultureInfo.InvariantCulture, "{0:HH\\:mm\\:ss}", ((object)(v))) : v.ToString("o"))), jsonValueReaderWriter: new JsonConvertedValueReaderWriter( - JsonTimeOnlyReaderWriter.Instance, + SqliteJsonTimeOnlyReaderWriter.Instance, new ValueConverter( TimeOnly (string v) => TimeOnly.Parse(v, CultureInfo.InvariantCulture, DateTimeStyles.None), string (TimeOnly v) => (v.Ticks % 10000000L == 0L ? string.Format(CultureInfo.InvariantCulture, "{0:HH\\:mm\\:ss}", ((object)(v))) : v.ToString("o"))))); @@ -14151,9 +14151,9 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas int (string v) => ((object)v).GetHashCode(), string (string v) => v), converter: new CollectionToJsonStringConverter(new JsonCollectionOfStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance)), + SqliteJsonTimeOnlyReaderWriter.Instance)), jsonValueReaderWriter: new JsonCollectionOfStructsReaderWriter( - JsonTimeOnlyReaderWriter.Instance), + SqliteJsonTimeOnlyReaderWriter.Instance), elementMapping: SqliteTimeOnlyTypeMapping.Default); var timeOnlyArrayElementType = timeOnlyArray.SetElementType(typeof(TimeOnly)); timeOnlyArrayElementType.TypeMapping = timeOnlyArray.TypeMapping.ElementTypeMapping; diff --git a/test/EFCore.Sqlite.FunctionalTests/Types/SqliteMiscellaneousTypeTest.cs b/test/EFCore.Sqlite.FunctionalTests/Types/SqliteMiscellaneousTypeTest.cs index 442772613a1..30282e9a745 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Types/SqliteMiscellaneousTypeTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Types/SqliteMiscellaneousTypeTest.cs @@ -43,9 +43,8 @@ public class GuidTypeFixture : SqliteTypeFixture public class SqliteByteArrayTypeTest(SqliteByteArrayTypeTest.ByteArrayTypeFixture fixture, ITestOutputHelper testOutputHelper) : RelationalTypeTestBase(fixture, testOutputHelper) { - // TODO: string representation discrepancy between our JSON and M.D.SQLite's string representation, see #36749. - public override Task Query_property_within_json() - => Assert.ThrowsAsync(() => base.Query_property_within_json()); + + public override Task Query_property_within_json() => base.Query_property_within_json(); public override async Task ExecuteUpdate_within_json_to_nonjson_column() { diff --git a/test/EFCore.Sqlite.FunctionalTests/Types/SqliteTemporalTypeTest.cs b/test/EFCore.Sqlite.FunctionalTests/Types/SqliteTemporalTypeTest.cs index d3655b6a938..a600f064897 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Types/SqliteTemporalTypeTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Types/SqliteTemporalTypeTest.cs @@ -57,9 +57,9 @@ public class DateTypeFixture : SqliteTypeFixture public class SqliteTimeOnlyTypeTest(SqliteTimeOnlyTypeTest.TimeTypeFixture fixture, ITestOutputHelper testOutputHelper) : RelationalTypeTestBase(fixture, testOutputHelper) { - // TODO: string representation discrepancy between our JSON and M.D.SQLite's string representation, see #36749. + public override Task Query_property_within_json() - => Assert.ThrowsAsync(() => base.Query_property_within_json()); + => base.Query_property_within_json(); public override async Task ExecuteUpdate_within_json_to_nonjson_column() { diff --git a/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs index b1905c54a5e..57537571af5 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Update/JsonUpdateSqliteTest.cs @@ -1146,8 +1146,8 @@ public override async Task Edit_two_properties_on_same_entity_updates_the_entire AssertSql( """ -@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteArray":"","TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2323-04-03","TestDateOnlyCollection":["3234-01-23","4331-01-21"],"TestDateTime":"2100-11-11 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2200-11-11 12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-123450.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":-3,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"05:07:08.0000000","TestTimeOnlyCollection":["13:42:23.0000000","07:17:25.0000000"],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,9223372036854775807]}' (Nullable = false) (Size = 2161) -@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteArray":"010203","TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2023-10-10","TestDateOnlyCollection":["1234-01-23","4321-01-21"],"TestDateTime":"2000-01-01 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2000-01-01 12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-1234567890.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"11:12:13.0000000","TestTimeOnlyCollection":["11:42:23.0000000","07:17:27.0000000"],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,9223372036854775807]}' (Nullable = false) (Size = 2197) +@p0='{"TestBoolean":false,"TestBooleanCollection":[true,false],"TestByte":25,"TestByteArray":"","TestByteCollection":null,"TestCharacter":"h","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2323-04-03","TestDateOnlyCollection":["3234-01-23","4331-01-21"],"TestDateTime":"2100-11-11 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2200-11-11 12:34:56-05:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-123450.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInCollection1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.2345,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-7],"TestGuid":"00000000-0000-0000-0000-000000000000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-12,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Baz","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":-3,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-7],"TestNullableInt32":90,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-18,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.4,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"05:07:08","TestTimeOnlyCollection":["13:42:23","07:17:25"],"TestTimeSpan":"6:05:04.003","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":12,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":12345,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567867,"TestUnsignedInt64Collection":[0,0,9223372036854775807]}' (Nullable = false) (Size = 2137) +@p1='{"TestBoolean":true,"TestBooleanCollection":[true,false],"TestByte":255,"TestByteArray":"010203","TestByteCollection":null,"TestCharacter":"a","TestCharacterCollection":["A","B","\u0022"],"TestDateOnly":"2023-10-10","TestDateOnlyCollection":["1234-01-23","4321-01-21"],"TestDateTime":"2000-01-01 12:34:56","TestDateTimeCollection":["2000-01-01 12:34:56","3000-01-01 12:34:56"],"TestDateTimeOffset":"2000-01-01 12:34:56-08:00","TestDateTimeOffsetCollection":["2000-01-01 12:34:56-08:00"],"TestDecimal":"-1234567890.01","TestDecimalCollection":["-1234567890.01"],"TestDefaultString":"MyDefaultStringInReference1","TestDefaultStringCollection":["S1","\u0022S2\u0022","S3"],"TestDouble":-1.23456789,"TestDoubleCollection":[-1.23456789,1.23456789,0],"TestEnum":-1,"TestEnumCollection":[-1,-3,-7],"TestEnumWithIntConverter":2,"TestEnumWithIntConverterCollection":[-1,-3,-7],"TestGuid":"12345678-1234-4321-7777-987654321000","TestGuidCollection":["12345678-1234-4321-7777-987654321000"],"TestInt16":-1234,"TestInt16Collection":[-32768,0,32767],"TestInt32":32,"TestInt32Collection":[-2147483648,0,2147483647],"TestInt64":64,"TestInt64Collection":[-9223372036854775808,0,9223372036854775807],"TestMaxLengthString":"Foo","TestMaxLengthStringCollection":["S1","S2","S3"],"TestNullableEnum":-1,"TestNullableEnumCollection":[-1,null,-3,-7],"TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithConverterThatHandlesNullsCollection":[-1,null,-7],"TestNullableEnumWithIntConverter":2,"TestNullableEnumWithIntConverterCollection":[-1,null,-3,-7],"TestNullableInt32":78,"TestNullableInt32Collection":[null,-2147483648,0,null,2147483647,null],"TestSignedByte":-128,"TestSignedByteCollection":[-128,0,127],"TestSingle":-1.234,"TestSingleCollection":[-1.234,0,-1.234],"TestTimeOnly":"11:12:13","TestTimeOnlyCollection":["11:42:23","07:17:27"],"TestTimeSpan":"10:09:08.007","TestTimeSpanCollection":["10:09:08.007","-9:50:51.993"],"TestUnsignedInt16":1234,"TestUnsignedInt16Collection":[0,0,65535],"TestUnsignedInt32":1234565789,"TestUnsignedInt32Collection":[0,0,4294967295],"TestUnsignedInt64":1234567890123456789,"TestUnsignedInt64Collection":[0,0,9223372036854775807]}' (Nullable = false) (Size = 2173) @p2='1' UPDATE "JsonEntitiesAllTypes" SET "Collection" = json_set("Collection", '$[0]', json(@p0)), "Reference" = @p1 @@ -1726,8 +1726,8 @@ public override async Task Edit_single_property_collection_of_timeonly() AssertSql( """ -@p0='["13:42:23.0000000","01:01:07.0000000"]' (Nullable = false) (Size = 39) -@p1='["01:01:07.0000000","07:17:27.0000000"]' (Nullable = false) (Size = 39) +@p0='["13:42:23","01:01:07"]' (Nullable = false) (Size = 23) +@p1='["01:01:07","07:17:27"]' (Nullable = false) (Size = 23) @p2='1' UPDATE "JsonEntitiesAllTypes" SET "Collection" = json_set("Collection", '$[0].TestTimeOnlyCollection', json(@p0)), "Reference" = json_set("Reference", '$.TestTimeOnlyCollection', json(@p1)) @@ -2295,9 +2295,9 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b var parameterSize = value switch { - true => "1562", - false => "1559", - _ => "1561" + true => "1554", + false => "1551", + _ => "1553" }; var updateParameter = value switch @@ -2310,7 +2310,7 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b AssertSql( @"@p0='[{""TestBoolean"":false,""TestBooleanCollection"":[],""TestByte"":0,""TestByteArray"":null,""TestByteCollection"":null,""TestCharacter"":""\u0000"",""TestCharacterCollection"":" + characterCollection - + @",""TestDateOnly"":""0001-01-01"",""TestDateOnlyCollection"":[],""TestDateTime"":""0001-01-01 00:00:00"",""TestDateTimeCollection"":[],""TestDateTimeOffset"":""0001-01-01 00:00:00+00:00"",""TestDateTimeOffsetCollection"":[],""TestDecimal"":""0.0"",""TestDecimalCollection"":[],""TestDefaultString"":null,""TestDefaultStringCollection"":[],""TestDouble"":0,""TestDoubleCollection"":[],""TestEnum"":0,""TestEnumCollection"":[],""TestEnumWithIntConverter"":0,""TestEnumWithIntConverterCollection"":[],""TestGuid"":""00000000-0000-0000-0000-000000000000"",""TestGuidCollection"":[],""TestInt16"":0,""TestInt16Collection"":[],""TestInt32"":0,""TestInt32Collection"":[],""TestInt64"":0,""TestInt64Collection"":[],""TestMaxLengthString"":null,""TestMaxLengthStringCollection"":[],""TestNullableEnum"":null,""TestNullableEnumCollection"":[],""TestNullableEnumWithConverterThatHandlesNulls"":null,""TestNullableEnumWithConverterThatHandlesNullsCollection"":[],""TestNullableEnumWithIntConverter"":null,""TestNullableEnumWithIntConverterCollection"":[],""TestNullableInt32"":null,""TestNullableInt32Collection"":[],""TestSignedByte"":0,""TestSignedByteCollection"":[],""TestSingle"":0,""TestSingleCollection"":[],""TestTimeOnly"":""00:00:00.0000000"",""TestTimeOnlyCollection"":[],""TestTimeSpan"":""0:00:00"",""TestTimeSpanCollection"":[],""TestUnsignedInt16"":0,""TestUnsignedInt16Collection"":[],""TestUnsignedInt32"":0,""TestUnsignedInt32Collection"":[],""TestUnsignedInt64"":0,""TestUnsignedInt64Collection"":[]}]' (Nullable = false) (Size = " + + @",""TestDateOnly"":""0001-01-01"",""TestDateOnlyCollection"":[],""TestDateTime"":""0001-01-01 00:00:00"",""TestDateTimeCollection"":[],""TestDateTimeOffset"":""0001-01-01 00:00:00+00:00"",""TestDateTimeOffsetCollection"":[],""TestDecimal"":""0.0"",""TestDecimalCollection"":[],""TestDefaultString"":null,""TestDefaultStringCollection"":[],""TestDouble"":0,""TestDoubleCollection"":[],""TestEnum"":0,""TestEnumCollection"":[],""TestEnumWithIntConverter"":0,""TestEnumWithIntConverterCollection"":[],""TestGuid"":""00000000-0000-0000-0000-000000000000"",""TestGuidCollection"":[],""TestInt16"":0,""TestInt16Collection"":[],""TestInt32"":0,""TestInt32Collection"":[],""TestInt64"":0,""TestInt64Collection"":[],""TestMaxLengthString"":null,""TestMaxLengthStringCollection"":[],""TestNullableEnum"":null,""TestNullableEnumCollection"":[],""TestNullableEnumWithConverterThatHandlesNulls"":null,""TestNullableEnumWithConverterThatHandlesNullsCollection"":[],""TestNullableEnumWithIntConverter"":null,""TestNullableEnumWithIntConverterCollection"":[],""TestNullableInt32"":null,""TestNullableInt32Collection"":[],""TestSignedByte"":0,""TestSignedByteCollection"":[],""TestSingle"":0,""TestSingleCollection"":[],""TestTimeOnly"":""00:00:00"",""TestTimeOnlyCollection"":[],""TestTimeSpan"":""0:00:00"",""TestTimeSpanCollection"":[],""TestUnsignedInt16"":0,""TestUnsignedInt16Collection"":[],""TestUnsignedInt32"":0,""TestUnsignedInt32Collection"":[],""TestUnsignedInt64"":0,""TestUnsignedInt64Collection"":[]}]' (Nullable = false) (Size = " + parameterSize + @") @p1='7624'