@@ -325,11 +325,24 @@ private List<IColumnModification> GenerateColumnModifications()
325
325
var jsonColumnTypeMapping = jsonColumn . StoreTypeMapping ;
326
326
var navigationValue = finalUpdatePathElement . ParentEntry . GetCurrentValue ( navigation ) ;
327
327
var jsonPathString = string . Join ( "." , updateInfo . Path . Select ( x => x . PropertyName + ( x . Ordinal != null ? "[" + x . Ordinal + "]" : "" ) ) ) ;
328
- object ? value ;
329
- if ( updateInfo . Property is not null )
328
+ if ( updateInfo . Property is IProperty property )
330
329
{
331
- value = GenerateValueForSinglePropertyUpdate ( updateInfo . Property , updateInfo . PropertyValue ) ;
332
- jsonPathString = jsonPathString + "." + updateInfo . Property . GetJsonPropertyName ( ) ;
330
+ var columnModificationParameters = new ColumnModificationParameters (
331
+ jsonColumn . Name ,
332
+ value : updateInfo . PropertyValue ,
333
+ property : property ,
334
+ columnType : jsonColumnTypeMapping . StoreType ,
335
+ jsonColumnTypeMapping ,
336
+ jsonPath : jsonPathString + "." + updateInfo . Property . GetJsonPropertyName ( ) ,
337
+ read : false ,
338
+ write : true ,
339
+ key : false ,
340
+ condition : false ,
341
+ _sensitiveLoggingEnabled ) { GenerateParameterName = _generateParameterName } ;
342
+
343
+ ProcessSinglePropertyJsonUpdate ( ref columnModificationParameters ) ;
344
+
345
+ columnModifications . Add ( new ColumnModification ( columnModificationParameters ) ) ;
333
346
}
334
347
else
335
348
{
@@ -371,26 +384,24 @@ private List<IColumnModification> GenerateColumnModifications()
371
384
372
385
writer . Flush ( ) ;
373
386
374
- value = writer . BytesCommitted > 0
387
+ var value = writer . BytesCommitted > 0
375
388
? Encoding . UTF8 . GetString ( stream . ToArray ( ) )
376
389
: null ;
377
- }
378
390
379
- var columnModificationParameters = new ColumnModificationParameters (
380
- jsonColumn . Name ,
381
- value : value ,
382
- property : updateInfo . Property ,
383
- columnType : jsonColumnTypeMapping . StoreType ,
384
- jsonColumnTypeMapping ,
385
- jsonPath : jsonPathString ,
386
- read : false ,
387
- write : true ,
388
- key : false ,
389
- condition : false ,
390
- _sensitiveLoggingEnabled )
391
- { GenerateParameterName = _generateParameterName , } ;
392
-
393
- columnModifications . Add ( new ColumnModification ( columnModificationParameters ) ) ;
391
+ columnModifications . Add ( new ColumnModification ( new ColumnModificationParameters (
392
+ jsonColumn . Name ,
393
+ value : value ,
394
+ property : updateInfo . Property ,
395
+ columnType : jsonColumnTypeMapping . StoreType ,
396
+ jsonColumnTypeMapping ,
397
+ jsonPath : jsonPathString ,
398
+ read : false ,
399
+ write : true ,
400
+ key : false ,
401
+ condition : false ,
402
+ _sensitiveLoggingEnabled )
403
+ { GenerateParameterName = _generateParameterName } ) ) ;
404
+ }
394
405
}
395
406
}
396
407
@@ -659,7 +670,7 @@ void HandleColumnModification(IColumnMappingBase columnMapping)
659
670
if ( modifiedMembers . Count == 1 )
660
671
{
661
672
result . Property = modifiedMembers . Single ( ) . Metadata ;
662
- result . PropertyValue = entry . GetCurrentProviderValue ( result . Property ) ;
673
+ result . PropertyValue = entry . GetCurrentValue ( result . Property ) ;
663
674
}
664
675
else
665
676
{
@@ -711,22 +722,56 @@ static JsonPartialUpdateInfo FindCommonJsonPartialUpdateInfo(
711
722
}
712
723
713
724
/// <summary>
714
- /// Generates value to use for update in case a single property is being updated .
725
+ /// Performs processing specifically needed for column modifications that correspond to single- property JSON updates .
715
726
/// </summary>
716
- /// <param name="property">Property to be updated.</param >
717
- /// <param name="propertyValue">Value object that the property will be updated to.</param>
718
- /// <returns>Value that the property will be updated to.</returns>
719
- [ EntityFrameworkInternal ]
720
- protected virtual object ? GenerateValueForSinglePropertyUpdate ( IProperty property , object ? propertyValue )
727
+ /// <remarks >
728
+ /// By default, strings, numeric types and bool and sent as a regular relational parameter, since database functions responsible for
729
+ /// patching JSON documents support this. Other types get converted to JSON via the normal means and sent as a string parameter.
730
+ /// </remarks>
731
+ protected virtual void ProcessSinglePropertyJsonUpdate ( ref ColumnModificationParameters parameters )
721
732
{
733
+ var property = parameters . Property ! ;
722
734
var propertyProviderClrType = ( property . GetTypeMapping ( ) . Converter ? . ProviderClrType ?? property . ClrType ) . UnwrapNullableType ( ) ;
723
735
724
- return ( propertyProviderClrType == typeof ( DateTime )
725
- || propertyProviderClrType == typeof ( DateTimeOffset )
726
- || propertyProviderClrType == typeof ( TimeSpan )
727
- || propertyProviderClrType == typeof ( Guid ) )
728
- ? JsonValue . Create ( propertyValue ) ? . ToJsonString ( ) . Replace ( "\" " , "" )
729
- : propertyValue ;
736
+ // On most databases, the function which patches a JSON document (e.g. SQL Server JSON_MODIFY) accepts relational string, numeric
737
+ // and bool types directly, without serializing it to a JSON string. So by default, for those cases simply return the value as-is,
738
+ // with the property's type mapping which will take care of sending the parameter with the relational value.
739
+ // Note that we haven't yet applied a value converter if one is configured, in order to allow for it to get applied later with
740
+ // the regular parameter flow.
741
+ if ( propertyProviderClrType == typeof ( string )
742
+ || propertyProviderClrType == typeof ( bool )
743
+ || propertyProviderClrType . IsNumeric ( ) )
744
+ {
745
+ parameters = parameters with { TypeMapping = property . GetRelationalTypeMapping ( ) } ;
746
+ return ;
747
+ }
748
+
749
+ // Other, non-JSON-native types need to be serialized to a JSON string.
750
+
751
+ // First, apply value conversion to get the provider value
752
+ var value = property . GetTypeMapping ( ) . Converter is ValueConverter converter
753
+ ? converter . ConvertToProvider ( parameters . Value )
754
+ : parameters . Value ;
755
+
756
+ var stream = new MemoryStream ( ) ;
757
+ var writer = new Utf8JsonWriter ( stream , new JsonWriterOptions { Indented = false } ) ;
758
+
759
+ if ( value is null )
760
+ {
761
+ writer . WriteNullValue ( ) ;
762
+ }
763
+ else
764
+ {
765
+ property . GetJsonValueReaderWriter ( ) ! . ToJson ( writer , value ) ;
766
+ }
767
+
768
+ writer . Flush ( ) ;
769
+
770
+ // The JSON string contains enclosing quotes (JSON string representation), remove these.
771
+ parameters = parameters with
772
+ {
773
+ Value = Encoding . UTF8 . GetString ( stream . ToArray ( ) ) [ 1 ..^ 1 ]
774
+ } ;
730
775
}
731
776
732
777
private void WriteJson (
0 commit comments