diff --git a/src/EFCore.Relational/Query/Internal/RelationalJsonUtilities.cs b/src/EFCore.Relational/Query/Internal/RelationalJsonUtilities.cs index 74c8c180a66..8b2fa7e2915 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalJsonUtilities.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalJsonUtilities.cs @@ -46,7 +46,7 @@ public static class RelationalJsonUtilities writer.Flush(); - return Encoding.UTF8.GetString(stream.ToArray()); + return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); void WriteJson(Utf8JsonWriter writer, IComplexType complexType, object? value, bool collection) { diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs index ccc4d1091ec..f2aaf2db6ce 100644 --- a/src/EFCore.Relational/Update/ModificationCommand.cs +++ b/src/EFCore.Relational/Update/ModificationCommand.cs @@ -822,7 +822,7 @@ void HandleJson(List columnModifications) writer.Flush(); var value = writer.BytesCommitted > 0 - ? Encoding.UTF8.GetString(stream.ToArray()) + ? Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length) : null; columnModifications.Add( diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs index d8bddfa9d95..4d84905d621 100644 --- a/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs +++ b/src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs @@ -43,7 +43,7 @@ public SqlServerModificationCommand(in NonTrackedModificationCommandParameters m /// protected override void ProcessSinglePropertyJsonUpdate(ref ColumnModificationParameters parameters) { - // See: Issue #34432 + // TODO: Move more of this logic to the type mapping. Issue #34432 var property = parameters.Property!; var mapping = property.GetRelationalTypeMapping(); var propertyProviderClrType = (mapping.Converter?.ProviderClrType ?? property.ClrType).UnwrapNullableType(); @@ -51,43 +51,48 @@ protected override void ProcessSinglePropertyJsonUpdate(ref ColumnModificationPa // JSON-compatible non-string values (bool, numeric, null) are sent directly as non-string parameters. if (value is null - || propertyProviderClrType == typeof(bool) - || propertyProviderClrType.IsNumeric()) + || ((propertyProviderClrType == typeof(bool) + || propertyProviderClrType.IsNumeric()) + && !property.IsPrimitiveCollection)) { parameters = parameters with { Value = value, TypeMapping = mapping }; + + return; } - else + + var jsonValueReaderWriter = mapping.JsonValueReaderWriter; + if (jsonValueReaderWriter != null) { - // Everything else must go as either a string parameter or a json parameter, depending on whether the json type - // is being used or not. To determine this, we get the JSON value and check if it is a string or some other - // type of JSON object. - var jsonValueReaderWriter = mapping.JsonValueReaderWriter; - if (jsonValueReaderWriter != null) + if (property.IsPrimitiveCollection) { - var stringValue = jsonValueReaderWriter.ToJsonString(value); - if (!stringValue.StartsWith('\"')) - { - // This is actual JSON, so send with the original type mapping, which may indicate the column type is JSON. - parameters = parameters with { Value = stringValue }; - - return; - } + // This is a JSON array, so send with the original type mapping, which may indicate the column type is JSON. + parameters = parameters with { Value = jsonValueReaderWriter.ToJsonString(value) }; - // Otherwise remove the quotes and send the value as a string. - value = stringValue[1..^1]; - } - else if (mapping.Converter != null) - { - value = mapping.Converter.ConvertToProvider(value); + return; } + // Otherwise, wrap the value in a simple JSON object to avoid double escaping. parameters = parameters with { - Value = value, + Value = jsonValueReaderWriter.ToJsonObjectString("", value), TypeMapping = parameters.TypeMapping is SqlServerStructuralJsonTypeMapping - ? SqlServerStringTypeMapping.UnicodeDefault - : parameters.TypeMapping + ? parameters.TypeMapping + : SqlServerStructuralJsonTypeMapping.Default }; + + return; + } + else if (mapping.Converter != null) + { + value = mapping.Converter.ConvertToProvider(value); } + + parameters = parameters with + { + Value = value, + TypeMapping = parameters.TypeMapping is SqlServerStructuralJsonTypeMapping + ? SqlServerStringTypeMapping.UnicodeDefault + : parameters.TypeMapping + }; } } diff --git a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs index 6c5d369bb6f..f43b2046dd0 100644 --- a/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs +++ b/src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs @@ -141,33 +141,42 @@ protected override void AppendUpdateColumnValue( string name, string? schema) { - if (columnModification.JsonPath is not (null or "$")) + if (columnModification.JsonPath is null or "$") { - stringBuilder.Append("JSON_MODIFY("); - updateSqlGeneratorHelper.DelimitIdentifier(stringBuilder, columnModification.ColumnName); + base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); + return; + } - // using strict so that we don't remove json elements when they are assigned NULL value - stringBuilder.Append(", 'strict "); - stringBuilder.Append(columnModification.JsonPath); - stringBuilder.Append("', "); + stringBuilder.Append("JSON_MODIFY("); + updateSqlGeneratorHelper.DelimitIdentifier(stringBuilder, columnModification.ColumnName); - if (columnModification.Property is { IsPrimitiveCollection: false }) - { - base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); - } - else - { - stringBuilder.Append("JSON_QUERY("); - base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); - stringBuilder.Append(")"); - } + // using strict so that we don't remove json elements when they are assigned NULL value + stringBuilder.Append(", 'strict "); + stringBuilder.Append(columnModification.JsonPath); + stringBuilder.Append("', "); + var mapping = columnModification.Property?.GetRelationalTypeMapping(); + var propertyProviderClrType = (mapping?.Converter?.ProviderClrType ?? columnModification.Property?.ClrType)?.UnwrapNullableType(); - stringBuilder.Append(")"); + if (columnModification.Value is null + || propertyProviderClrType == typeof(bool) + || (propertyProviderClrType != null && propertyProviderClrType.IsNumeric())) + { + base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); + } + else if (columnModification.Property is { IsPrimitiveCollection: false }) + { + stringBuilder.Append("JSON_VALUE("); + base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); + stringBuilder.Append(", '$.\"\"')"); } else { + stringBuilder.Append("JSON_QUERY("); base.AppendUpdateColumnValue(updateSqlGeneratorHelper, columnModification, stringBuilder, name, schema); + stringBuilder.Append(")"); } + + stringBuilder.Append(")"); } /// diff --git a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs index 24600a0a93c..f61f541659d 100644 --- a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs +++ b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs @@ -86,9 +86,38 @@ public string ToJsonString(object value) ToJson(writer, value); writer.Flush(); - var buffer = stream.ToArray(); - return Encoding.UTF8.GetString(buffer); + return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); + } + + /// + /// Returns a string containg a JSON object whith the specified property and value. + /// + /// The property name. + /// The value to write. + /// The JSON representation of a JSON object whith the specified property and value. + public virtual string ToJsonObjectString(string propertyName, object? value) + { + Check.NotNull(propertyName); + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + writer.WriteStartObject(); + writer.WritePropertyName(propertyName); + if (value == null) + { + writer.WriteNullValue(); + } + else + { + ToJson(writer, value); + } + + writer.WriteEndObject(); + writer.Flush(); + + return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); } /// diff --git a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs index 9bb7b3b351d..54c37c517d0 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/JsonUpdateTestBase.cs @@ -3624,6 +3624,53 @@ public virtual Task Add_and_update_nested_optional_primitive_collection(bool? va } }); + [ConditionalFact] + public virtual Task Edit_single_property_with_non_ascii_characters() + => TestHelpers.ExecuteWithStrategyInTransactionAsync( + CreateContext, + UseTransaction, + async context => + { + var query = await context.JsonEntitiesBasic.ToListAsync(); + var entity = query.Single(x => x.Id == 1); + entity.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething = "测试1"; + + var newEntity = new JsonEntityBasic + { + Id = 3, + Name = "ComprehensiveEntity", + OwnedCollectionRoot = [], + OwnedReferenceRoot = new JsonOwnedRoot + { + Name = "ReferenceRoot", + Number = 300, + OwnedCollectionBranch = [], + OwnedReferenceBranch = new JsonOwnedBranch + { + Id = 15, + Date = new DateTime(2023, 10, 5), + Enum = JsonEnum.Three, + Fraction = 99.99m, + OwnedCollectionLeaf = [], + OwnedReferenceLeaf = new JsonOwnedLeaf { SomethingSomething = "测试1" } + } + } + }; + + context.Add(newEntity); + + ClearLog(); + await context.SaveChangesAsync(); + }, + async context => + { + var result = await context.Set().SingleAsync(x => x.Id == 3); + Assert.Equal("测试1", result.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething); + + result = await context.Set().SingleAsync(x => x.Id == 1); + Assert.Equal("测试1", result.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething); + }); + public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); diff --git a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs index a78a5b8ad60..87b45536ada 100644 --- a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs +++ b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs @@ -3773,9 +3773,7 @@ protected string ToJsonPropertyString(JsonValueReaderWriter jsonReaderWriter, ob writer.WriteEndObject(); writer.Flush(); - var buffer = stream.ToArray(); - - return Encoding.UTF8.GetString(buffer); + return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); } protected object? FromJsonPropertyString(JsonValueReaderWriter jsonReaderWriter, string value, object? existingValue = null) diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs index 791720b9abf..33eb74beb23 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerCondition.cs @@ -22,5 +22,5 @@ public enum SqlServerCondition SupportsFunctions2019 = 1 << 13, SupportsFunctions2022 = 1 << 14, SupportsJsonType = 1 << 15, - SupportsVectorType = 1 << 15, + SupportsVectorType = 1 << 16, } diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs index 57c9f61dc65..6d25944439a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateJsonTypeSqlServerTest.cs @@ -251,7 +251,7 @@ public override async Task Delete_json_collection_branch() SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -295,7 +295,7 @@ public override async Task Delete_json_reference_leaf() SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -334,12 +334,12 @@ public override async Task Edit_element_in_json_collection_branch() AssertSql( """ -@p0='2111-11-11T00:00:00' (Nullable = false) (Size = 4000) +@p0='{"":"2111-11-11T00:00:00"}' (Nullable = false) (Size = 26) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -356,12 +356,12 @@ public override async Task Edit_element_in_json_collection_root1() AssertSql( """ -@p0='Modified' (Nullable = false) (Size = 4000) +@p0='{"":"Modified"}' (Nullable = false) (Size = 15) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -378,12 +378,12 @@ public override async Task Edit_element_in_json_collection_root2() AssertSql( """ -@p0='Modified' (Nullable = false) (Size = 4000) +@p0='{"":"Modified"}' (Nullable = false) (Size = 15) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -605,12 +605,12 @@ public override async Task Edit_single_property_char() AssertSql( """ -@p0='t' (Nullable = false) (Size = 4000) +@p0='{"":"t"}' (Nullable = false) (Size = 8) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', @p0) +UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -622,19 +622,55 @@ FROM [JsonEntitiesAllTypes] AS [j] """); } + + public override async Task Edit_single_property_with_non_ascii_characters() + { + await base.Edit_single_property_with_non_ascii_characters(); + + AssertSql( + """ +@p0='{"":"\u6D4B\u8BD51"}' (Nullable = false) (Size = 20) +@p1='1' +@p2='{"Id":0,"Name":"ReferenceRoot","Names":null,"Number":300,"Numbers":null,"OwnedCollectionBranch":[],"OwnedReferenceBranch":{"Date":"2023-10-05T00:00:00","Enum":-3,"Enums":null,"Fraction":99.99,"Id":15,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[],"OwnedReferenceLeaf":{"SomethingSomething":"\u6D4B\u8BD51"}}}' (Nullable = false) (Size = 327) +@p3='[]' (Nullable = false) (Size = 2) +@p4='3' +@p5=NULL (DbType = Int32) +@p6='ComprehensiveEntity' (Size = 4000) + +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething', JSON_VALUE(@p0, '$.""')) +OUTPUT 1 +WHERE [Id] = @p1; +INSERT INTO [JsonEntitiesBasic] ([OwnedReferenceRoot], [OwnedCollectionRoot], [Id], [EntityBasicId], [Name]) +VALUES (@p2, @p3, @p4, @p5, @p6); +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE [j].[Id] = 3 +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE [j].[Id] = 1 +"""); + } + public override async Task Edit_single_property_datetime() { await base.Edit_single_property_datetime(); AssertSql( """ -@p0='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) -@p1='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) +@p0='{"":"3000-01-01T12:34:56"}' (Nullable = false) (Size = 26) +@p1='{"":"3000-01-01T12:34:56"}' (Nullable = false) (Size = 26) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -652,13 +688,13 @@ public override async Task Edit_single_property_datetimeoffset() AssertSql( """ -@p0='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) -@p1='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) +@p0='{"":"3000-01-01T12:34:56-04:00"}' (Nullable = false) (Size = 32) +@p1='{"":"3000-01-01T12:34:56-04:00"}' (Nullable = false) (Size = 32) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffset', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffset', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -724,13 +760,13 @@ public override async Task Edit_single_property_guid() AssertSql( """ -@p0='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) -@p1='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) +@p0='{"":"12345678-1234-4321-5555-987654321000"}' (Nullable = false) (Size = 43) +@p1='{"":"12345678-1234-4321-5555-987654321000"}' (Nullable = false) (Size = 43) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuid', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuid', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -868,13 +904,13 @@ public override async Task Edit_single_property_timespan() AssertSql( """ -@p0='10:01:01.007' (Nullable = false) (Size = 4000) -@p1='10:01:01.007' (Nullable = false) (Size = 4000) +@p0='{"":"10:01:01.007"}' (Nullable = false) (Size = 19) +@p1='{"":"10:01:01.007"}' (Nullable = false) (Size = 19) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -964,13 +1000,13 @@ public override async Task Edit_single_property_dateonly() AssertSql( """ -@p0='2000-02-04' (Nullable = false) (Size = 4000) -@p1='1023-01-01' (Nullable = false) (Size = 4000) +@p0='{"":"2000-02-04"}' (Nullable = false) (Size = 17) +@p1='{"":"1023-01-01"}' (Nullable = false) (Size = 17) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateOnly', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateOnly', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateOnly', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateOnly', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1203,13 +1239,13 @@ public override async Task Edit_single_property_nullable_enum_with_converter_tha AssertSql( """ -@p0='Three' (Nullable = false) (Size = 4000) -@p1='One' (Nullable = false) (Size = 4000) +@p0='{"":"Three"}' (Nullable = false) (Size = 12) +@p1='{"":"One"}' (Nullable = false) (Size = 10) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1364,12 +1400,12 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Tr AssertSql( """ -@p0='True' (Nullable = false) (Size = 4000) +@p0='{"":"True"}' (Nullable = false) (Size = 11) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringTrueFalse', @p0) +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringTrueFalse', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -1387,12 +1423,12 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Y_ AssertSql( """ -@p0='N' (Nullable = false) (Size = 4000) +@p0='{"":"N"}' (Nullable = false) (Size = 8) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringYN', @p0) +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringYN', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -1528,13 +1564,13 @@ public override async Task Edit_single_property_collection_of_byte() AssertSql( """ -@p0='Dg==' (Nullable = false) (Size = 4000) -@p1='GRo=' (Nullable = false) (Size = 4000) +@p0='{"":"Dg=="}' (Nullable = false) (Size = 11) +@p1='{"":"GRo="}' (Nullable = false) (Size = 11) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByteCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByteCollection', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByteCollection', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByteCollection', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1991,7 +2027,7 @@ public override async Task Edit_single_property_collection_of_nullable_int32_set SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2087,7 +2123,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_set_ SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2135,7 +2171,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2183,7 +2219,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2264,7 +2300,7 @@ FROM [JsonEntitiesBasic] AS [j] SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -2448,7 +2484,6 @@ FROM [JsonEntitiesBasic] AS [j] } } - [ConditionalTheory(Skip = "TODO:SQLJSON Hangs (See InsertsHang.cs")] public override async Task Add_and_update_nested_optional_primitive_collection(bool? value) { await base.Add_and_update_nested_optional_primitive_collection(value); @@ -2474,6 +2509,25 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b _ => "'[]'" }; + var updateParameterSize = value switch + { + true => "4000", + false => "5", + _ => "2" + }; + + var updatedP0 = "@p0=" + + updateParameter + + @" (Nullable = false) (Size = " + + updateParameterSize + + @")"; + + var updatedP0Reference = value switch + { + true => "@p0", + _ => "JSON_QUERY(@p0)" + }; + AssertSql( @"@p0='[{""TestBoolean"":false,""TestBooleanCollection"":[],""TestByte"":0,""TestByteArray"":null,""TestByteCollection"":null,""TestCharacter"":""\u0000"",""TestCharacterCollection"":" + characterCollection @@ -2481,31 +2535,31 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b + parameterSize + @") @p1='7624' -@p2='[]' (Size = 4000) -@p3=NULL (Size = 8000) (DbType = Binary) -@p4='[]' (Size = 4000) -@p5='[]' (Size = 4000) -@p6='[]' (Size = 4000) -@p7='[]' (Size = 4000) -@p8='[]' (Size = 4000) -@p9='[]' (Size = 4000) -@p10='[]' (Size = 4000) -@p11='[]' (Size = 4000) -@p12='[]' (Nullable = false) (Size = 4000) -@p13='[]' (Size = 4000) -@p14='[]' (Size = 4000) -@p15='[]' (Size = 4000) -@p16='[]' (Size = 4000) -@p17='[]' (Size = 4000) -@p18=NULL (Size = 4000) -@p19='[]' (Size = 4000) -@p20='[]' (Size = 4000) -@p21='[]' (Size = 4000) -@p22='[]' (Size = 4000) -@p23='[]' (Size = 4000) -@p24='[]' (Size = 4000) -@p25='[]' (Size = 4000) -@p26='[]' (Size = 4000) +@p2='[]' (Size = 8000) +@p3=NULL (Size = 8000) +@p4='[]' (Size = 8000) +@p5='[]' (Size = 8000) +@p6='[]' (Size = 8000) +@p7='[]' (Size = 8000) +@p8='[]' (Size = 8000) +@p9='[]' (Size = 8000) +@p10='[]' (Size = 8000) +@p11='[]' (Size = 8000) +@p12='[]' (Nullable = false) (Size = 8000) +@p13='[]' (Size = 8000) +@p14='[]' (Size = 8000) +@p15='[]' (Size = 8000) +@p16='[]' (Size = 8000) +@p17='[]' (Size = 8000) +@p18=NULL (Size = 8000) +@p19='[]' (Size = 8000) +@p20='[]' (Size = 8000) +@p21='[]' (Size = 8000) +@p22='[]' (Size = 8000) +@p23='[]' (Size = 8000) +@p24='[]' (Size = 8000) +@p25='[]' (Size = 8000) +@p26='[]' (Size = 8000) SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; @@ -2518,14 +2572,15 @@ FROM [JsonEntitiesAllTypes] AS [j] WHERE [j].[Id] = 7624 """, // - "@p0=" - + updateParameter - + @" (Nullable = false) (Size = 4000) + updatedP0 + + @" @p1='7624' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', " ++ updatedP0Reference ++ @") OUTPUT 1 WHERE [Id] = @p1;", // @@ -2583,13 +2638,13 @@ public override async Task Edit_single_property_timeonly() AssertSql( """ -@p0='01:01:07.0000000' (Nullable = false) (Size = 4000) -@p1='01:01:07.0000000' (Nullable = false) (Size = 4000) +@p0='{"":"01:01:07.0000000"}' (Nullable = false) (Size = 23) +@p1='{"":"01:01:07.0000000"}' (Nullable = false) (Size = 23) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeOnly', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeOnly', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeOnly', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeOnly', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs index 4edfe672c02..8d6856d6194 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs @@ -247,7 +247,7 @@ public override async Task Delete_json_collection_branch() SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -291,7 +291,7 @@ public override async Task Delete_json_reference_leaf() SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -330,12 +330,12 @@ public override async Task Edit_element_in_json_collection_branch() AssertSql( """ -@p0='2111-11-11T00:00:00' (Nullable = false) (Size = 4000) +@p0='{"":"2111-11-11T00:00:00"}' (Nullable = false) (Size = 26) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].OwnedCollectionBranch[0].Date', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -352,12 +352,12 @@ public override async Task Edit_element_in_json_collection_root1() AssertSql( """ -@p0='Modified' (Nullable = false) (Size = 4000) +@p0='{"":"Modified"}' (Nullable = false) (Size = 15) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[0].Name', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -374,12 +374,12 @@ public override async Task Edit_element_in_json_collection_root2() AssertSql( """ -@p0='Modified' (Nullable = false) (Size = 4000) +@p0='{"":"Modified"}' (Nullable = false) (Size = 15) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', @p0) +UPDATE [JsonEntitiesBasic] SET [OwnedCollectionRoot] = JSON_MODIFY([OwnedCollectionRoot], 'strict $[1].Name', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -601,12 +601,12 @@ public override async Task Edit_single_property_char() AssertSql( """ -@p0='t' (Nullable = false) (Size = 4000) +@p0='{"":"t"}' (Nullable = false) (Size = 8) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', @p0) +UPDATE [JsonEntitiesAllTypes] SET [Reference] = JSON_MODIFY([Reference], 'strict $.TestCharacter', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -618,19 +618,54 @@ FROM [JsonEntitiesAllTypes] AS [j] """); } + public override async Task Edit_single_property_with_non_ascii_characters() + { + await base.Edit_single_property_with_non_ascii_characters(); + + AssertSql( + """ +@p0='{"":"\u6D4B\u8BD51"}' (Nullable = false) (Size = 20) +@p1='1' +@p2='{"Id":0,"Name":"ReferenceRoot","Names":null,"Number":300,"Numbers":null,"OwnedCollectionBranch":[],"OwnedReferenceBranch":{"Date":"2023-10-05T00:00:00","Enum":-3,"Enums":null,"Fraction":99.99,"Id":15,"NullableEnum":null,"NullableEnums":null,"OwnedCollectionLeaf":[],"OwnedReferenceLeaf":{"SomethingSomething":"\u6D4B\u8BD51"}}}' (Nullable = false) (Size = 327) +@p3='[]' (Nullable = false) (Size = 2) +@p4='3' +@p5=NULL (DbType = Int32) +@p6='ComprehensiveEntity' (Size = 4000) + +SET NOCOUNT ON; +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething', JSON_VALUE(@p0, '$.""')) +OUTPUT 1 +WHERE [Id] = @p1; +INSERT INTO [JsonEntitiesBasic] ([OwnedReferenceRoot], [OwnedCollectionRoot], [Id], [EntityBasicId], [Name]) +VALUES (@p2, @p3, @p4, @p5, @p6); +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE [j].[Id] = 3 +""", + // + """ +SELECT TOP(2) [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot] +FROM [JsonEntitiesBasic] AS [j] +WHERE [j].[Id] = 1 +"""); + } + public override async Task Edit_single_property_datetime() { await base.Edit_single_property_datetime(); AssertSql( """ -@p0='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) -@p1='3000-01-01T12:34:56' (Nullable = false) (Size = 4000) +@p0='{"":"3000-01-01T12:34:56"}' (Nullable = false) (Size = 26) +@p1='{"":"3000-01-01T12:34:56"}' (Nullable = false) (Size = 26) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTime', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTime', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -648,13 +683,13 @@ public override async Task Edit_single_property_datetimeoffset() AssertSql( """ -@p0='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) -@p1='3000-01-01T12:34:56-04:00' (Nullable = false) (Size = 4000) +@p0='{"":"3000-01-01T12:34:56-04:00"}' (Nullable = false) (Size = 32) +@p1='{"":"3000-01-01T12:34:56-04:00"}' (Nullable = false) (Size = 32) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffset', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestDateTimeOffset', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestDateTimeOffset', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -720,13 +755,13 @@ public override async Task Edit_single_property_guid() AssertSql( """ -@p0='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) -@p1='12345678-1234-4321-5555-987654321000' (Nullable = false) (Size = 4000) +@p0='{"":"12345678-1234-4321-5555-987654321000"}' (Nullable = false) (Size = 43) +@p1='{"":"12345678-1234-4321-5555-987654321000"}' (Nullable = false) (Size = 43) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuid', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestGuid', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestGuid', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -864,13 +899,13 @@ public override async Task Edit_single_property_timespan() AssertSql( """ -@p0='10:01:01.007' (Nullable = false) (Size = 4000) -@p1='10:01:01.007' (Nullable = false) (Size = 4000) +@p0='{"":"10:01:01.007"}' (Nullable = false) (Size = 19) +@p1='{"":"10:01:01.007"}' (Nullable = false) (Size = 19) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestTimeSpan', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestTimeSpan', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1152,13 +1187,13 @@ public override async Task Edit_single_property_nullable_enum_with_converter_tha AssertSql( """ -@p0='Three' (Nullable = false) (Size = 4000) -@p1='One' (Nullable = false) (Size = 4000) +@p0='{"":"Three"}' (Nullable = false) (Size = 12) +@p1='{"":"One"}' (Nullable = false) (Size = 10) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNulls', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNulls', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1313,12 +1348,12 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Tr AssertSql( """ -@p0='True' (Nullable = false) (Size = 4000) +@p0='{"":"True"}' (Nullable = false) (Size = 11) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringTrueFalse', @p0) +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringTrueFalse', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -1336,12 +1371,12 @@ public override async Task Edit_single_property_with_converter_bool_to_string_Y_ AssertSql( """ -@p0='N' (Nullable = false) (Size = 4000) +@p0='{"":"N"}' (Nullable = false) (Size = 8) @p1='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringYN', @p0) +UPDATE [JsonEntitiesConverters] SET [Reference] = JSON_MODIFY([Reference], 'strict $.BoolConvertedToStringYN', JSON_VALUE(@p0, '$.""')) OUTPUT 1 WHERE [Id] = @p1; """, @@ -1477,13 +1512,13 @@ public override async Task Edit_single_property_collection_of_byte() AssertSql( """ -@p0='Dg==' (Nullable = false) (Size = 4000) -@p1='GRo=' (Nullable = false) (Size = 4000) +@p0='{"":"Dg=="}' (Nullable = false) (Size = 11) +@p1='{"":"GRo="}' (Nullable = false) (Size = 11) @p2='1' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByteCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByteCollection', @p1) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestByteCollection', JSON_VALUE(@p0, '$.""')), [Reference] = JSON_MODIFY([Reference], 'strict $.TestByteCollection', JSON_VALUE(@p1, '$.""')) OUTPUT 1 WHERE [Id] = @p2; """, @@ -1939,7 +1974,7 @@ public override async Task Edit_single_property_collection_of_nullable_int32_set SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableInt32Collection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableInt32Collection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2035,7 +2070,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_set_ SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2083,7 +2118,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithIntConverterCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithIntConverterCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2131,7 +2166,7 @@ public override async Task Edit_single_property_collection_of_nullable_enum_with SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p0)), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', JSON_QUERY(@p1)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestNullableEnumWithConverterThatHandlesNullsCollection', @p0), [Reference] = JSON_MODIFY([Reference], 'strict $.TestNullableEnumWithConverterThatHandlesNullsCollection', @p1) OUTPUT 1 WHERE [Id] = @p2; """, @@ -2309,7 +2344,7 @@ FROM [JsonEntitiesBasic] AS [j] SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesBasic] SET [OwnedReferenceRoot] = JSON_MODIFY([OwnedReferenceRoot], 'strict $.OwnedCollectionBranch', @p0) OUTPUT 1 WHERE [Id] = @p1; """, @@ -2429,12 +2464,27 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b _ => "'[]'" }; + var p0 = @"@p0='[{""TestBoolean"":false,""TestBooleanCollection"":[],""TestByte"":0,""TestByteArray"":null,""TestByteCollection"":null,""TestCharacter"":""\u0000"",""TestCharacterCollection"":" ++ characterCollection ++ @",""TestDateOnly"":""0001-01-01"",""TestDateOnlyCollection"":[],""TestDateTime"":""0001-01-01T00:00:00"",""TestDateTimeCollection"":[],""TestDateTimeOffset"":""0001-01-01T00:00:00+00:00"",""TestDateTimeOffsetCollection"":[],""TestDecimal"":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 = " ++ parameterSize ++ @")"; + + var updatedP0 = "@p0=" + + updateParameter + + @" (Nullable = false) (Size = " + + updateParameterSize + + @")"; + + var updatedP0Reference = value switch + { + true => "@p0", + _ => "JSON_QUERY(@p0)" + }; + AssertSql( - @"@p0='[{""TestBoolean"":false,""TestBooleanCollection"":[],""TestByte"":0,""TestByteArray"":null,""TestByteCollection"":null,""TestCharacter"":""\u0000"",""TestCharacterCollection"":" - + characterCollection - + @",""TestDateOnly"":""0001-01-01"",""TestDateOnlyCollection"":[],""TestDateTime"":""0001-01-01T00:00:00"",""TestDateTimeCollection"":[],""TestDateTimeOffset"":""0001-01-01T00:00:00+00:00"",""TestDateTimeOffsetCollection"":[],""TestDecimal"":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 = " - + parameterSize - + @") + $""" +{p0} @p1='7624' @p2='[]' (Size = 4000) @p3=NULL (Size = 8000) (DbType = Binary) @@ -2465,7 +2515,8 @@ public override async Task Add_and_update_nested_optional_primitive_collection(b SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; INSERT INTO [JsonEntitiesAllTypes] ([Collection], [Id], [TestBooleanCollection], [TestByteCollection], [TestCharacterCollection], [TestDateTimeCollection], [TestDateTimeOffsetCollection], [TestDecimalCollection], [TestDefaultStringCollection], [TestDoubleCollection], [TestEnumCollection], [TestEnumWithIntConverterCollection], [TestGuidCollection], [TestInt16Collection], [TestInt32Collection], [TestInt64Collection], [TestMaxLengthStringCollection], [TestNullableEnumCollection], [TestNullableEnumWithConverterThatHandlesNullsCollection], [TestNullableEnumWithIntConverterCollection], [TestNullableInt32Collection], [TestSignedByteCollection], [TestSingleCollection], [TestTimeSpanCollection], [TestUnsignedInt16Collection], [TestUnsignedInt32Collection], [TestUnsignedInt64Collection]) -VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26);", +VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26); +""", // """ SELECT TOP(2) [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] @@ -2473,18 +2524,16 @@ FROM [JsonEntitiesAllTypes] AS [j] WHERE [j].[Id] = 7624 """, // - "@p0=" - + updateParameter - + @" (Nullable = false) (Size = " - + updateParameterSize - + @") + $""" +{updatedP0} @p1='7624' SET IMPLICIT_TRANSACTIONS OFF; SET NOCOUNT ON; -UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', JSON_QUERY(@p0)) +UPDATE [JsonEntitiesAllTypes] SET [Collection] = JSON_MODIFY([Collection], 'strict $[0].TestCharacterCollection', {updatedP0Reference}) OUTPUT 1 -WHERE [Id] = @p1;", +WHERE [Id] = @p1; +""", // """ SELECT TOP(2) [j].[Id], [j].[TestBooleanCollection], [j].[TestByteCollection], [j].[TestCharacterCollection], [j].[TestDateTimeCollection], [j].[TestDateTimeOffsetCollection], [j].[TestDecimalCollection], [j].[TestDefaultStringCollection], [j].[TestDoubleCollection], [j].[TestEnumCollection], [j].[TestEnumWithIntConverterCollection], [j].[TestGuidCollection], [j].[TestInt16Collection], [j].[TestInt32Collection], [j].[TestInt64Collection], [j].[TestMaxLengthStringCollection], [j].[TestNullableEnumCollection], [j].[TestNullableEnumWithConverterThatHandlesNullsCollection], [j].[TestNullableEnumWithIntConverterCollection], [j].[TestNullableInt32Collection], [j].[TestSignedByteCollection], [j].[TestSingleCollection], [j].[TestTimeSpanCollection], [j].[TestUnsignedInt16Collection], [j].[TestUnsignedInt32Collection], [j].[TestUnsignedInt64Collection], [j].[Collection], [j].[Reference] diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs index a1dc6279f2e..82d11973e64 100644 --- a/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs +++ b/test/EFCore.SqlServer.HierarchyId.Tests/TypeMappingTests.cs @@ -80,13 +80,11 @@ private void Convert_to_and_from_JSON( writer.WriteEndObject(); writer.Flush(); - var buffer = stream.ToArray(); - - var actual = Encoding.UTF8.GetString(buffer); + var actual = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); Assert.Equal(json, actual); - var readerManager = new Utf8JsonReaderManager(new JsonReaderData(buffer), null); + var readerManager = new Utf8JsonReaderManager(new JsonReaderData(stream.ToArray()), null); readerManager.MoveNext(); readerManager.MoveNext(); readerManager.MoveNext();