@@ -3381,6 +3381,183 @@ public void Complex_property_json_column_is_nullable_in_TPH_hierarchy()
33813381 Assert . IsType < JsonColumn > ( jsonColumn ) ;
33823382 }
33833383
3384+ [ Fact ]
3385+ public void Complex_property_json_column_is_not_duplicated_in_TPT_child_tables ( )
3386+ {
3387+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3388+
3389+ modelBuilder . Entity < TptBaseEntityWithComplexProperty > ( )
3390+ . UseTptMappingStrategy ( )
3391+ . ComplexProperty ( e => e . ComplexProperty , b => b . ToJson ( ) ) ;
3392+ modelBuilder . Entity < TptDerivedEntityWithoutComplexProperty > ( ) ;
3393+
3394+ var model = modelBuilder . FinalizeModel ( ) ;
3395+ var relationalModel = model . GetRelationalModel ( ) ;
3396+
3397+ var baseTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptBaseEntityWithComplexProperty ) ) ;
3398+ var childTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptDerivedEntityWithoutComplexProperty ) ) ;
3399+
3400+ // The JSON column for the base complex property must appear only in the base table
3401+ Assert . Contains ( baseTable . Columns , c => c . Name == nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3402+ Assert . DoesNotContain ( childTable . Columns , c => c . Name == nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3403+ }
3404+
3405+ [ Fact ]
3406+ public void Complex_property_columns_are_not_duplicated_in_TPT_child_tables ( )
3407+ {
3408+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3409+
3410+ modelBuilder . Entity < TptBaseEntityWithComplexProperty > ( )
3411+ . UseTptMappingStrategy ( )
3412+ . ComplexProperty ( e => e . ComplexProperty ) ;
3413+ modelBuilder . Entity < TptDerivedEntityWithoutComplexProperty > ( ) ;
3414+
3415+ var model = modelBuilder . FinalizeModel ( ) ;
3416+ var relationalModel = model . GetRelationalModel ( ) ;
3417+
3418+ var baseTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptBaseEntityWithComplexProperty ) ) ;
3419+ var childTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptDerivedEntityWithoutComplexProperty ) ) ;
3420+
3421+ // Non-JSON complex property columns appear only on the base table.
3422+ var valueColumnName = nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) + "_" + nameof ( ComplexData . Value ) ;
3423+ var numberColumnName = nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) + "_" + nameof ( ComplexData . Number ) ;
3424+ Assert . Contains ( baseTable . Columns , c => c . Name == valueColumnName ) ;
3425+ Assert . Contains ( baseTable . Columns , c => c . Name == numberColumnName ) ;
3426+ Assert . DoesNotContain ( childTable . Columns , c => c . Name == valueColumnName ) ;
3427+ Assert . DoesNotContain ( childTable . Columns , c => c . Name == numberColumnName ) ;
3428+ }
3429+
3430+ [ Fact ]
3431+ public void Complex_property_json_column_is_created_in_every_TPC_table ( )
3432+ {
3433+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3434+
3435+ modelBuilder . Entity < TpcBaseEntityWithComplexProperty > ( b =>
3436+ {
3437+ b . UseTpcMappingStrategy ( ) ;
3438+ b . ComplexProperty ( e => e . ComplexProperty , cb => cb . ToJson ( ) ) ;
3439+ } ) ;
3440+ modelBuilder . Entity < TpcDerivedEntityWithoutComplexProperty > ( ) ;
3441+
3442+ var model = modelBuilder . FinalizeModel ( ) ;
3443+ var relationalModel = model . GetRelationalModel ( ) ;
3444+
3445+ var baseTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TpcBaseEntityWithComplexProperty ) ) ;
3446+ var derivedTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TpcDerivedEntityWithoutComplexProperty ) ) ;
3447+
3448+ // In TPC the JSON container column appears on every concrete table.
3449+ Assert . Contains ( baseTable . Columns , c => c . Name == nameof ( TpcBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3450+ Assert . Contains ( derivedTable . Columns , c => c . Name == nameof ( TpcBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3451+ }
3452+
3453+ [ Fact ]
3454+ public void GetContainerColumnName_with_StoreObjectIdentifier_resolves_per_table ( )
3455+ {
3456+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3457+
3458+ modelBuilder . Entity < TptBaseEntityWithComplexProperty > ( )
3459+ . UseTptMappingStrategy ( )
3460+ . ComplexProperty ( e => e . ComplexProperty , cb => cb . ToJson ( ) ) ;
3461+ modelBuilder . Entity < TptDerivedEntityWithoutComplexProperty > ( ) ;
3462+
3463+ var model = modelBuilder . FinalizeModel ( ) ;
3464+ var baseEntity = model . FindEntityType ( typeof ( TptBaseEntityWithComplexProperty ) ) ! ;
3465+ var complexProperty = baseEntity . FindComplexProperty ( nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) ) ! ;
3466+ var complexType = complexProperty . ComplexType ;
3467+
3468+ var baseTable = StoreObjectIdentifier . Table ( nameof ( TptBaseEntityWithComplexProperty ) ) ;
3469+ var childTable = StoreObjectIdentifier . Table ( nameof ( TptDerivedEntityWithoutComplexProperty ) ) ;
3470+ var unrelatedTable = StoreObjectIdentifier . Table ( "SomeOtherTable" ) ;
3471+
3472+ Assert . Equal ( nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) , complexType . GetContainerColumnName ( baseTable ) ) ;
3473+ Assert . Null ( complexType . GetContainerColumnName ( childTable ) ) ;
3474+ Assert . Null ( complexType . GetContainerColumnName ( unrelatedTable ) ) ;
3475+ }
3476+
3477+ [ Fact ]
3478+ public void GetContainerColumnName_with_StoreObjectIdentifier_returns_column_for_every_TPC_table ( )
3479+ {
3480+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3481+
3482+ modelBuilder . Entity < TpcBaseEntityWithComplexProperty > ( b =>
3483+ {
3484+ b . UseTpcMappingStrategy ( ) ;
3485+ b . ComplexProperty ( e => e . ComplexProperty , cb => cb . ToJson ( ) ) ;
3486+ } ) ;
3487+ modelBuilder . Entity < TpcDerivedEntityWithoutComplexProperty > ( ) ;
3488+
3489+ var model = modelBuilder . FinalizeModel ( ) ;
3490+ var baseEntity = model . FindEntityType ( typeof ( TpcBaseEntityWithComplexProperty ) ) ! ;
3491+ var complexProperty = baseEntity . FindComplexProperty ( nameof ( TpcBaseEntityWithComplexProperty . ComplexProperty ) ) ! ;
3492+ var complexType = complexProperty . ComplexType ;
3493+
3494+ var baseTable = StoreObjectIdentifier . Table ( nameof ( TpcBaseEntityWithComplexProperty ) ) ;
3495+ var derivedTable = StoreObjectIdentifier . Table ( nameof ( TpcDerivedEntityWithoutComplexProperty ) ) ;
3496+ var unrelatedTable = StoreObjectIdentifier . Table ( "SomeOtherTable" ) ;
3497+
3498+ Assert . Equal ( nameof ( TpcBaseEntityWithComplexProperty . ComplexProperty ) , complexType . GetContainerColumnName ( baseTable ) ) ;
3499+ Assert . Equal ( nameof ( TpcBaseEntityWithComplexProperty . ComplexProperty ) , complexType . GetContainerColumnName ( derivedTable ) ) ;
3500+ Assert . Null ( complexType . GetContainerColumnName ( unrelatedTable ) ) ;
3501+ }
3502+
3503+ [ Fact ]
3504+ public void Complex_type_with_PK_property_creates_column_mapping_in_TPT_child_table ( )
3505+ {
3506+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3507+
3508+ modelBuilder . Entity < TptBaseWithComplexTypePK > ( b =>
3509+ {
3510+ b . UseTptMappingStrategy ( ) ;
3511+ b . ComplexProperty ( e => e . Key ) ;
3512+ b . HasKey ( e => e . Key . Id ) ;
3513+ b . Property ( e => e . Key . Id ) . ValueGeneratedNever ( ) ;
3514+ } ) ;
3515+ modelBuilder . Entity < TptDerivedWithComplexTypePK > ( ) ;
3516+
3517+ var model = modelBuilder . FinalizeModel ( ) ;
3518+ var relationalModel = model . GetRelationalModel ( ) ;
3519+
3520+ var baseTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptBaseWithComplexTypePK ) ) ;
3521+ var childTable = relationalModel . Tables . Single ( t => t . Name == nameof ( TptDerivedWithComplexTypePK ) ) ;
3522+
3523+ // The PK column for the complex-typed key must appear in both the base table and the child table.
3524+ Assert . Contains ( baseTable . Columns , c => c . Name == "Key_Id" ) ;
3525+ Assert . Contains ( childTable . Columns , c => c . Name == "Key_Id" ) ;
3526+
3527+ // Non-key properties of the derived entity stay on the child table.
3528+ Assert . Contains ( childTable . Columns , c => c . Name == nameof ( TptDerivedWithComplexTypePK . Extra ) ) ;
3529+ Assert . DoesNotContain ( baseTable . Columns , c => c . Name == nameof ( TptDerivedWithComplexTypePK . Extra ) ) ;
3530+ }
3531+
3532+ [ Fact ]
3533+ public void Complex_property_json_column_is_not_duplicated_in_TPT_child_views ( )
3534+ {
3535+ var modelBuilder = CreateConventionModelBuilder ( ) ;
3536+
3537+ modelBuilder . Entity < TptBaseEntityWithComplexProperty > ( b =>
3538+ {
3539+ b . UseTptMappingStrategy ( ) ;
3540+ b . ToTable ( nameof ( TptBaseEntityWithComplexProperty ) ) ;
3541+ b . ToView ( nameof ( TptBaseEntityWithComplexProperty ) + "View" ) ;
3542+ b . ComplexProperty ( e => e . ComplexProperty , cb => cb . ToJson ( ) ) ;
3543+ } ) ;
3544+ modelBuilder . Entity < TptDerivedEntityWithoutComplexProperty > ( b =>
3545+ {
3546+ b . ToTable ( nameof ( TptDerivedEntityWithoutComplexProperty ) ) ;
3547+ b . ToView ( nameof ( TptDerivedEntityWithoutComplexProperty ) + "View" ) ;
3548+ } ) ;
3549+
3550+ var model = modelBuilder . FinalizeModel ( ) ;
3551+ var relationalModel = model . GetRelationalModel ( ) ;
3552+
3553+ var baseView = relationalModel . Views . Single ( v => v . Name == nameof ( TptBaseEntityWithComplexProperty ) + "View" ) ;
3554+ var childView = relationalModel . Views . Single ( v => v . Name == nameof ( TptDerivedEntityWithoutComplexProperty ) + "View" ) ;
3555+
3556+ // The JSON column for the base complex property must appear only in the base view.
3557+ Assert . Contains ( baseView . Columns , c => c . Name == nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3558+ Assert . DoesNotContain ( childView . Columns , c => c . Name == nameof ( TptBaseEntityWithComplexProperty . ComplexProperty ) ) ;
3559+ }
3560+
33843561 [ Fact ]
33853562 public void Json_element_tree_is_built_for_owned_entity_json_columns ( )
33863563 {
@@ -3994,6 +4171,37 @@ private class TphEntityWithComplexProperty : TphBaseEntity
39944171 public ComplexData ComplexProperty { get ; set ; }
39954172 }
39964173
4174+ private abstract class TptBaseEntityWithComplexProperty
4175+ {
4176+ public int Id { get ; set ; }
4177+ public ComplexData ComplexProperty { get ; set ; }
4178+ }
4179+
4180+ private class TptDerivedEntityWithoutComplexProperty : TptBaseEntityWithComplexProperty ;
4181+
4182+ private class TpcBaseEntityWithComplexProperty
4183+ {
4184+ public int Id { get ; set ; }
4185+ public ComplexData ComplexProperty { get ; set ; }
4186+ }
4187+
4188+ private class TpcDerivedEntityWithoutComplexProperty : TpcBaseEntityWithComplexProperty ;
4189+
4190+ private abstract class TptBaseWithComplexTypePK
4191+ {
4192+ public TptComplexKey Key { get ; set ; } = null ! ;
4193+ }
4194+
4195+ private class TptComplexKey
4196+ {
4197+ public int Id { get ; set ; }
4198+ }
4199+
4200+ private class TptDerivedWithComplexTypePK : TptBaseWithComplexTypePK
4201+ {
4202+ public int Extra { get ; set ; }
4203+ }
4204+
39974205 private class EntityWithJsonOwnedWithCollection
39984206 {
39994207 public int Id { get ; set ; }
0 commit comments