@@ -54,6 +54,7 @@ protected override FormattableString BuildDescription()
5454 }
5555
5656 private readonly bool _isAbstract ;
57+ private readonly bool _isMultiLevelDiscriminator ;
5758
5859 private readonly CSharpType _additionalBinaryDataPropsFieldType = typeof ( IDictionary < string , BinaryData > ) ;
5960 private readonly Type _additionalPropsUnknownType = typeof ( BinaryData ) ;
@@ -68,6 +69,7 @@ public ModelProvider(InputModelType inputModel) : base(inputModel)
6869 {
6970 _inputModel = inputModel ;
7071 _isAbstract = _inputModel . DiscriminatorProperty is not null && _inputModel . DiscriminatorValue is null ;
72+ _isMultiLevelDiscriminator = ComputeIsMultiLevelDiscriminator ( ) ;
7173
7274 if ( _inputModel . BaseModel is not null )
7375 {
@@ -524,12 +526,6 @@ protected override ConstructorProvider[] BuildConstructors()
524526 return [ FullConstructor ] ;
525527 }
526528
527- // Check if this model needs the dual constructor pattern
528- if ( ShouldHaveDualConstructorPattern ( ) )
529- {
530- return BuildDualConstructorPattern ( ) ;
531- }
532-
533529 // Build the standard single initialization constructor
534530 var accessibility = DeclarationModifiers . HasFlag ( TypeSignatureModifiers . Abstract )
535531 ? MethodSignatureModifiers . Private | MethodSignatureModifiers . Protected
@@ -551,23 +547,33 @@ protected override ConstructorProvider[] BuildConstructors()
551547 } ,
552548 this ) ;
553549
550+ var constructors = new List < ConstructorProvider > { constructor } ;
551+
552+ // Add FullConstructor if parameters are different
554553 if ( ! constructorParameters . SequenceEqual ( FullConstructor . Signature . Parameters ) )
555554 {
556- return [ constructor , FullConstructor ] ;
555+ constructors . Add ( FullConstructor ) ;
556+ }
557+
558+ // For multi-level discriminators, add one additional private protected constructor
559+ if ( _isMultiLevelDiscriminator )
560+ {
561+ var protectedConstructor = BuildProtectedInheritanceConstructor ( ) ;
562+ constructors . Add ( protectedConstructor ) ;
557563 }
558564
559- return [ constructor ] ;
565+ return [ .. constructors ] ;
560566 }
561567
562568 /// <summary>
563569 /// Determines if this model should have a dual constructor pattern.
564570 /// This is needed when the model shares the same discriminator property name as its base model
565571 /// AND has derived models, indicating it's an intermediate type in a discriminated union hierarchy.
566572 /// </summary>
567- private bool ShouldHaveDualConstructorPattern ( )
573+ private bool ComputeIsMultiLevelDiscriminator ( )
568574 {
569575 // Only applies to non-abstract models with a base model
570- if ( _isAbstract || BaseModelProvider == null )
576+ if ( _isAbstract || _inputModel . BaseModel == null )
571577 {
572578 return false ;
573579 }
@@ -584,7 +590,7 @@ private bool ShouldHaveDualConstructorPattern()
584590 }
585591
586592 // Check if base model has a discriminator property with the same name
587- if ( BaseModelProvider . _inputModel . DiscriminatorProperty == null )
593+ if ( _inputModel . BaseModel . DiscriminatorProperty == null )
588594 {
589595 return false ;
590596 }
@@ -593,119 +599,30 @@ private bool ShouldHaveDualConstructorPattern()
593599 // and this model has derived models, it needs the dual constructor pattern
594600 return string . Equals (
595601 _inputModel . DiscriminatorProperty . Name ,
596- BaseModelProvider . _inputModel . DiscriminatorProperty . Name ,
602+ _inputModel . BaseModel . DiscriminatorProperty . Name ,
597603 StringComparison . OrdinalIgnoreCase ) ;
598604 }
599605
600606 /// <summary>
601- /// Builds the dual constructor pattern for models that share discriminator properties with their base.
602- /// Creates three constructors:
603- /// 1. Public constructor for external use
604- /// 2. Private protected constructor for derived models to call
605- /// 3. Internal constructor for serialization
607+ /// Builds a private protected constructor for multi-level discriminator inheritance.
608+ /// This allows derived models to call this constructor with their discriminator value.
606609 /// </summary>
607- private ConstructorProvider [ ] BuildDualConstructorPattern ( )
610+ private ConstructorProvider BuildProtectedInheritanceConstructor ( )
608611 {
609- // Build public constructor (without discriminator parameter)
610- var ( publicParams , publicInitializer ) = BuildPublicInstantiationParameters ( ) ;
611- var publicConstructor = new ConstructorProvider (
612- signature : new ConstructorSignature (
613- Type ,
614- $ "Initializes a new instance of { Type : C} ",
615- MethodSignatureModifiers . Public ,
616- publicParams ,
617- initializer : publicInitializer ) ,
618- bodyStatements : new MethodBodyStatement [ ]
619- {
620- GetPropertyInitializers ( true , parameters : publicParams )
621- } ,
622- this ) ;
612+ var ( parameters , initializer ) = BuildConstructorParameters ( true , includeDiscriminatorParameter : true ) ;
623613
624- // Build private protected constructor (with discriminator parameter)
625- var ( protectedParams , protectedInitializer ) = BuildPrivateProtectedInheritanceParameters ( ) ;
626- var protectedConstructor = new ConstructorProvider (
614+ return new ConstructorProvider (
627615 signature : new ConstructorSignature (
628616 Type ,
629617 $ "Initializes a new instance of { Type : C} ",
630618 MethodSignatureModifiers . Private | MethodSignatureModifiers . Protected ,
631- protectedParams ,
632- initializer : protectedInitializer ) ,
619+ parameters ,
620+ initializer : initializer ) ,
633621 bodyStatements : new MethodBodyStatement [ ]
634622 {
635- GetPropertyInitializers ( true , parameters : protectedParams )
623+ GetPropertyInitializers ( true , parameters : parameters )
636624 } ,
637625 this ) ;
638-
639- // Internal constructor is the full constructor
640- return [ publicConstructor , protectedConstructor , FullConstructor ] ;
641- }
642-
643- /// <summary>
644- /// Builds parameters for public instantiation constructor (filters out discriminator).
645- /// </summary>
646- private ( IReadOnlyList < ParameterProvider > Parameters , ConstructorInitializer ? Initializer ) BuildPublicInstantiationParameters ( )
647- {
648- var ( standardParams , _) = BuildConstructorParameters ( true ) ;
649-
650- var parameters = standardParams . Where ( p => p . Property == null || ! p . Property . IsDiscriminator ) . ToList ( ) ;
651-
652- var initializer = CreatePrivateProtectedConstructorCall ( parameters ) ;
653-
654- return ( parameters , initializer ) ;
655- }
656-
657- /// <summary>
658- /// Creates a constructor initializer that calls the private protected constructor with discriminator value.
659- /// </summary>
660- private ConstructorInitializer CreatePrivateProtectedConstructorCall ( IReadOnlyList < ParameterProvider > parameters )
661- {
662- var discriminatorValue = EnsureDiscriminatorValueExpression ( ) ;
663- var args = new List < ValueExpression > ( ) ;
664-
665- if ( discriminatorValue != null )
666- {
667- args . Add ( discriminatorValue ) ;
668- }
669-
670- var overriddenProperties = CanonicalView . Properties . Where ( p => p . BaseProperty is not null ) . Select ( p => p . BaseProperty ! ) . ToHashSet ( ) ;
671- args . AddRange ( parameters . Select ( p => GetExpressionForCtor ( p , overriddenProperties , true ) ) ) ;
672-
673- return new ConstructorInitializer ( false , args . ToArray ( ) ) ;
674- }
675-
676- /// <summary>
677- /// Builds parameters for private protected inheritance constructor (includes discriminator).
678- /// </summary>
679- private ( IReadOnlyList < ParameterProvider > Parameters , ConstructorInitializer ? Initializer ) BuildPrivateProtectedInheritanceParameters ( )
680- {
681- var ( parameters , standardInitializer ) = BuildConstructorParameters ( true , includeDiscriminatorParameter : true ) ;
682-
683- var initializer = CreateBaseConstructorCallWithDiscriminatorParameter ( standardInitializer , parameters . FirstOrDefault ( ) ) ;
684-
685- return ( parameters , initializer ) ;
686- }
687-
688- /// <summary>
689- /// Creates a base constructor call that uses the discriminator parameter.
690- /// </summary>
691- private ConstructorInitializer ? CreateBaseConstructorCallWithDiscriminatorParameter (
692- ConstructorInitializer ? standardInitializer ,
693- ParameterProvider ? discriminatorParam )
694- {
695- if ( BaseModelProvider ? . Type is null || standardInitializer is null || discriminatorParam is null )
696- return standardInitializer ;
697-
698- var originalArgs = standardInitializer . Arguments ;
699- if ( originalArgs . Count == 0 )
700- return standardInitializer ;
701-
702- var newArgs = new List < ValueExpression >
703- {
704- discriminatorParam . AsVariable ( )
705- } ;
706- newArgs . AddRange ( originalArgs . Skip ( 1 ) ) ;
707-
708- return new ConstructorInitializer ( standardInitializer . IsBase , newArgs ) ;
709626 }
710627
711628 /// <summary>
@@ -818,7 +735,7 @@ private IEnumerable<FieldProvider> GetAllBaseFieldsForConstructorInitialization(
818735 if ( baseParameters . Count > 0 )
819736 {
820737 // Check if base model has dual constructor pattern and we should call private protected constructor
821- if ( isPrimaryConstructor && BaseModelProvider . ShouldHaveDualConstructorPattern ( ) )
738+ if ( isPrimaryConstructor && BaseModelProvider . _isMultiLevelDiscriminator )
822739 {
823740 // Call base model's private protected constructor with discriminator value
824741 var args = new List < ValueExpression > ( ) ;
@@ -853,8 +770,7 @@ private IEnumerable<FieldProvider> GetAllBaseFieldsForConstructorInitialization(
853770 ? baseParameters
854771 : baseParameters . Where ( p =>
855772 p . Property is null
856- || ( p . Property . IsDiscriminator && ! overriddenProperties . Contains ( p . Property ) && ( ! isPrimaryConstructor || includeDiscriminatorParameter ) )
857- || ( ! p . Property . IsDiscriminator && ! overriddenProperties . Contains ( p . Property ) ) ) ) ;
773+ || ( ! overriddenProperties . Contains ( p . Property ! ) && ( ! p . Property . IsDiscriminator || ! isPrimaryConstructor || includeDiscriminatorParameter ) ) ) ) ;
858774
859775 if ( includeDiscriminatorParameter && _inputModel . DiscriminatorProperty != null )
860776 {
0 commit comments