7
7
using System . Text . Json ;
8
8
using Microsoft . EntityFrameworkCore . ChangeTracking . Internal ;
9
9
using Microsoft . EntityFrameworkCore . Internal ;
10
+ using Microsoft . EntityFrameworkCore . Metadata ;
10
11
using Microsoft . EntityFrameworkCore . Metadata . Internal ;
11
12
using IColumnMapping = Microsoft . EntityFrameworkCore . Metadata . IColumnMapping ;
12
13
using ITableMapping = Microsoft . EntityFrameworkCore . Metadata . ITableMapping ;
@@ -243,7 +244,7 @@ private sealed class JsonPartialUpdateInfo
243
244
public object ? PropertyValue { get ; set ; }
244
245
}
245
246
246
- private record struct JsonPartialUpdatePathEntry ( string PropertyName , int ? Ordinal , IUpdateEntry ParentEntry , INavigation Navigation ) ;
247
+ private record struct JsonPartialUpdatePathEntry ( string PropertyName , int ? Ordinal , IUpdateEntry ParentEntry , INavigation ? Navigation , IComplexProperty ? ComplexProperty = null ) ;
247
248
248
249
private List < IColumnModification > GenerateColumnModifications ( )
249
250
{
@@ -263,7 +264,7 @@ private List<IColumnModification> GenerateColumnModifications()
263
264
{
264
265
Check . DebugAssert ( StoreStoredProcedure is null , "Multiple entries/shared identity not supported with stored procedures" ) ;
265
266
266
- sharedTableColumnMap = new Dictionary < string , ColumnValuePropagator > ( ) ;
267
+ sharedTableColumnMap = [ ] ;
267
268
268
269
if ( _comparer != null
269
270
&& _entries . Count > 1 )
@@ -297,6 +298,7 @@ private List<IColumnModification> GenerateColumnModifications()
297
298
if ( ! jsonEntry )
298
299
{
299
300
if ( entry . EntityType . IsMappedToJson ( )
301
+ || entry . EntityType . GetFlattenedComplexProperties ( ) . Any ( cp => cp . ComplexType . IsMappedToJson ( ) )
300
302
|| entry . EntityType . GetNavigations ( ) . Any ( e => e . IsCollection && e . TargetEntityType . IsMappedToJson ( ) ) )
301
303
{
302
304
jsonEntry = true ;
@@ -654,7 +656,8 @@ static JsonPartialUpdateInfo FindCommonJsonPartialUpdateInfo(
654
656
first . Path [ i ] . PropertyName ,
655
657
null ,
656
658
first . Path [ i ] . ParentEntry ,
657
- first . Path [ i ] . Navigation ) ;
659
+ Navigation : first . Path [ i ] . Navigation ,
660
+ ComplexProperty : first . Path [ i ] . ComplexProperty ) ;
658
661
659
662
result . Path . Add ( common ) ;
660
663
@@ -671,11 +674,15 @@ void HandleJson(List<IColumnModification> columnModifications)
671
674
{
672
675
var jsonColumnsUpdateMap = new Dictionary < IColumn , JsonPartialUpdateInfo > ( ) ;
673
676
var processedEntries = new List < IUpdateEntry > ( ) ;
674
- foreach ( var entry in _entries . Where ( e => e . EntityType . IsMappedToJson ( ) ) )
677
+ foreach ( var entry in _entries )
675
678
{
679
+ if ( ! entry . EntityType . IsMappedToJson ( ) )
680
+ {
681
+ continue ;
682
+ }
683
+
676
684
var jsonColumn = GetTableMapping ( entry . EntityType ) ! . Table . FindColumn ( entry . EntityType . GetContainerColumnName ( ) ! ) ! ;
677
685
var jsonPartialUpdateInfo = FindJsonPartialUpdateInfo ( entry , processedEntries ) ;
678
-
679
686
if ( jsonPartialUpdateInfo == null )
680
687
{
681
688
continue ;
@@ -691,8 +698,13 @@ void HandleJson(List<IColumnModification> columnModifications)
691
698
jsonColumnsUpdateMap [ jsonColumn ] = jsonPartialUpdateInfo ;
692
699
}
693
700
694
- foreach ( var entry in _entries . Where ( e => ! e . EntityType . IsMappedToJson ( ) ) )
701
+ foreach ( var entry in _entries )
695
702
{
703
+ if ( entry . EntityType . IsMappedToJson ( ) )
704
+ {
705
+ continue ;
706
+ }
707
+
696
708
foreach ( var jsonCollectionNavigation in entry . EntityType . GetNavigations ( )
697
709
. Where (
698
710
n => n . IsCollection
@@ -712,14 +724,32 @@ void HandleJson(List<IColumnModification> columnModifications)
712
724
jsonColumnsUpdateMap [ jsonCollectionColumn ] = jsonPartialUpdateInfo ;
713
725
}
714
726
}
727
+
728
+ foreach ( var complexProperty in entry . EntityType . GetFlattenedComplexProperties ( )
729
+ . Where ( cp => cp . ComplexType . IsMappedToJson ( ) && ! cp . DeclaringType . IsMappedToJson ( ) ) )
730
+ {
731
+ var complexType = complexProperty . ComplexType ;
732
+ var jsonColumn = GetTableMapping ( entry . EntityType ) ! . Table . FindColumn ( complexType . GetContainerColumnName ( ) ! ) ! ;
733
+
734
+ if ( ! jsonColumnsUpdateMap . ContainsKey ( jsonColumn ) )
735
+ {
736
+ var jsonPartialUpdateInfo = new JsonPartialUpdateInfo ( ) ;
737
+ jsonPartialUpdateInfo . Path . Insert ( 0 , new JsonPartialUpdatePathEntry ( "$" , null , entry , Navigation : null , ComplexProperty : complexProperty ) ) ;
738
+ jsonPartialUpdateInfo . PropertyValue = entry . GetCurrentValue ( complexProperty ) ;
739
+ jsonColumnsUpdateMap [ jsonColumn ] = jsonPartialUpdateInfo ;
740
+ }
741
+ }
715
742
}
716
743
717
744
foreach ( var ( jsonColumn , updateInfo ) in jsonColumnsUpdateMap )
718
745
{
719
746
var finalUpdatePathElement = updateInfo . Path . Last ( ) ;
720
747
var navigation = finalUpdatePathElement . Navigation ;
748
+ var complexProperty = finalUpdatePathElement . ComplexProperty ;
721
749
var jsonColumnTypeMapping = jsonColumn . StoreTypeMapping ;
722
- var navigationValue = finalUpdatePathElement . ParentEntry . GetCurrentValue ( navigation ) ;
750
+ var jsonContainerProperty = ( IPropertyBase ? ) navigation ?? complexProperty ;
751
+ var navigationValue = finalUpdatePathElement . ParentEntry . GetCurrentValue ( jsonContainerProperty ! ) ;
752
+
723
753
var jsonPathString = string . Join (
724
754
"." , updateInfo . Path . Select ( x => x . PropertyName + ( x . Ordinal != null ? "[" + x . Ordinal + "]" : "" ) ) ) ;
725
755
if ( updateInfo . Property is IProperty property )
@@ -755,8 +785,8 @@ void HandleJson(List<IColumnModification> columnModifications)
755
785
WriteJson (
756
786
writer ,
757
787
navigationValueElement ,
758
- finalUpdatePathElement . ParentEntry ,
759
- navigation . TargetEntityType ,
788
+ ( IInternalEntry ) finalUpdatePathElement . ParentEntry ,
789
+ ( ( IPropertyBase ? ) navigation ) ?? complexProperty ! ,
760
790
ordinal : null ,
761
791
isCollection : false ,
762
792
isTopLevel : true ) ;
@@ -769,13 +799,14 @@ void HandleJson(List<IColumnModification> columnModifications)
769
799
}
770
800
else
771
801
{
802
+ var propertyBase = ( ( IPropertyBase ? ) navigation ) ?? complexProperty ! ;
772
803
WriteJson (
773
804
writer ,
774
805
navigationValue ,
775
- finalUpdatePathElement . ParentEntry ,
776
- navigation . TargetEntityType ,
806
+ ( IInternalEntry ) finalUpdatePathElement . ParentEntry ,
807
+ ( ( IPropertyBase ? ) navigation ) ?? complexProperty ! ,
777
808
ordinal : null ,
778
- isCollection : navigation . IsCollection ,
809
+ isCollection : propertyBase . IsCollection ,
779
810
isTopLevel : true ) ;
780
811
}
781
812
@@ -840,16 +871,20 @@ protected virtual void ProcessSinglePropertyJsonUpdate(ref ColumnModificationPar
840
871
}
841
872
}
842
873
874
+ #pragma warning disable EF1001 // Internal EF Core API usage.
843
875
private void WriteJson (
844
876
Utf8JsonWriter writer ,
845
- object ? navigationValue ,
846
- IUpdateEntry parentEntry ,
847
- IEntityType entityType ,
877
+ object ? value ,
878
+ IInternalEntry parentEntry ,
879
+ IPropertyBase property ,
848
880
int ? ordinal ,
849
881
bool isCollection ,
850
882
bool isTopLevel )
851
883
{
852
- if ( navigationValue == null )
884
+ var structuralType = property is INavigation navigation
885
+ ? ( ITypeBase ) navigation . TargetEntityType
886
+ : ( ( IComplexProperty ) property ) . ComplexType ;
887
+ if ( value is null )
853
888
{
854
889
if ( ! isTopLevel )
855
890
{
@@ -861,15 +896,15 @@ private void WriteJson(
861
896
862
897
if ( isCollection )
863
898
{
864
- var i = 1 ;
899
+ var i = 0 ;
865
900
writer . WriteStartArray ( ) ;
866
- foreach ( var collectionElement in ( IEnumerable ) navigationValue )
901
+ foreach ( var collectionElement in ( IEnumerable ) value )
867
902
{
868
903
WriteJson (
869
904
writer ,
870
905
collectionElement ,
871
906
parentEntry ,
872
- entityType ,
907
+ property ,
873
908
i ++ ,
874
909
isCollection : false ,
875
910
isTopLevel : false ) ;
@@ -879,63 +914,89 @@ private void WriteJson(
879
914
return ;
880
915
}
881
916
882
- #pragma warning disable EF1001 // Internal EF Core API usage.
883
- var entry = ( IUpdateEntry ) ( ( InternalEntityEntry ) parentEntry ) . StateManager . TryGetEntry ( navigationValue , entityType ) ! ;
884
- #pragma warning restore EF1001 // Internal EF Core API usage.
885
-
886
917
writer . WriteStartObject ( ) ;
887
- foreach ( var property in entityType . GetFlattenedProperties ( ) )
918
+
919
+ var entry = structuralType is IComplexType complexType
920
+ ? complexType . ComplexProperty . IsCollection
921
+ ? parentEntry . GetComplexCollectionEntry ( complexType . ComplexProperty , ordinal ! . Value )
922
+ : parentEntry
923
+ : ( ( InternalEntityEntry ) parentEntry ) . StateManager . TryGetEntry ( value , ( IEntityType ) structuralType ) ! ;
924
+ WriteJsonObject ( writer , parentEntry , entry , structuralType , ordinal ) ;
925
+
926
+ writer . WriteEndObject ( ) ;
927
+ }
928
+
929
+ private void WriteJsonObject ( Utf8JsonWriter writer , IInternalEntry parentEntry , IInternalEntry entry , ITypeBase structuralType , int ? ordinal )
930
+ {
931
+ foreach ( var property in structuralType . GetProperties ( ) )
888
932
{
889
933
if ( property . IsKey ( ) )
890
934
{
891
935
if ( property . IsOrdinalKeyProperty ( ) && ordinal != null )
892
936
{
893
- entry . SetStoreGeneratedValue ( property , ordinal . Value , setModified : false ) ;
937
+ entry . SetStoreGeneratedValue ( property , ordinal . Value + 1 , setModified : false ) ;
894
938
}
895
939
896
940
continue ;
897
941
}
898
942
899
943
// jsonPropertyName can only be null for key properties
900
944
var jsonPropertyName = property . GetJsonPropertyName ( ) ! ;
901
- var value = entry . GetCurrentValue ( property ) ;
945
+ var propertyValue = entry . GetCurrentValue ( property ) ;
902
946
writer . WritePropertyName ( jsonPropertyName ) ;
903
947
904
- if ( value is not null )
948
+ if ( propertyValue is not null )
905
949
{
906
950
var jsonValueReaderWriter = property . GetJsonValueReaderWriter ( ) ?? property . GetTypeMapping ( ) . JsonValueReaderWriter ;
907
951
Check . DebugAssert ( jsonValueReaderWriter is not null , "Missing JsonValueReaderWriter on JSON property" ) ;
908
- jsonValueReaderWriter . ToJson ( writer , value ) ;
952
+ jsonValueReaderWriter . ToJson ( writer , propertyValue ) ;
909
953
}
910
954
else
911
955
{
912
956
writer . WriteNullValue ( ) ;
913
957
}
914
958
}
915
959
916
- foreach ( var navigation in entityType . GetNavigations ( ) )
960
+ foreach ( var complexProperty in structuralType . GetComplexProperties ( ) )
917
961
{
918
- // skip back-references to the parent
919
- if ( navigation . IsOnDependent )
920
- {
921
- continue ;
922
- }
923
-
924
- var jsonPropertyName = navigation . TargetEntityType . GetJsonPropertyName ( ) ! ;
925
- var ownedNavigationValue = entry . GetCurrentValue ( navigation ) ! ;
926
-
962
+ var jsonPropertyName = complexProperty . GetJsonPropertyName ( ) ! ;
963
+ var complexPropertyValue = entry . GetCurrentValue ( complexProperty ) ;
927
964
writer . WritePropertyName ( jsonPropertyName ) ;
965
+
928
966
WriteJson (
929
967
writer ,
930
- ownedNavigationValue ,
968
+ complexPropertyValue ,
931
969
entry ,
932
- navigation . TargetEntityType ,
970
+ complexProperty ,
933
971
ordinal : null ,
934
- isCollection : navigation . IsCollection ,
972
+ isCollection : complexProperty . IsCollection ,
935
973
isTopLevel : false ) ;
936
974
}
937
975
938
- writer . WriteEndObject ( ) ;
976
+ if ( structuralType is IEntityType entityType )
977
+ {
978
+ foreach ( var navigation in entityType . GetNavigations ( ) )
979
+ {
980
+ // skip back-references to the parent
981
+ if ( navigation . IsOnDependent )
982
+ {
983
+ continue ;
984
+ }
985
+
986
+ var jsonPropertyName = navigation . TargetEntityType . GetJsonPropertyName ( ) ! ;
987
+ var ownedNavigationValue = entry . GetCurrentValue ( navigation ) ! ;
988
+
989
+ writer . WritePropertyName ( jsonPropertyName ) ;
990
+ WriteJson (
991
+ writer ,
992
+ ownedNavigationValue ,
993
+ entry ,
994
+ navigation ,
995
+ ordinal : null ,
996
+ isCollection : navigation . IsCollection ,
997
+ isTopLevel : false ) ;
998
+ }
999
+ }
939
1000
}
940
1001
941
1002
private ITableMapping ? GetTableMapping ( ITypeBase structuralType )
0 commit comments