@@ -79,6 +79,19 @@ public override void OnAfterInitialize(
79
79
objectType ,
80
80
objectTypeCfg ,
81
81
discoveryContext ) ;
82
+ return ;
83
+ }
84
+
85
+ if ( discoveryContext . Type is InterfaceType interfaceType
86
+ && configuration is InterfaceTypeConfiguration interfaceTypeCfg )
87
+ {
88
+ ApplyMethodLevelReferenceResolvers (
89
+ interfaceType ,
90
+ interfaceTypeCfg ) ;
91
+
92
+ AggregatePropertyLevelKeyDirectives (
93
+ interfaceTypeCfg ,
94
+ discoveryContext ) ;
82
95
}
83
96
}
84
97
@@ -345,12 +358,23 @@ public override void OnAfterMakeExecutable(
345
358
{
346
359
CompleteExternalFieldSetters ( type , typeCfg ) ;
347
360
CompleteReferenceResolver ( typeCfg ) ;
361
+ return ;
362
+ }
363
+
364
+ if ( completionContext . Type is InterfaceType interfaceType
365
+ && configuration is InterfaceTypeConfiguration interfaceTypeCfg )
366
+ {
367
+ CompleteExternalFieldSetters ( interfaceType , interfaceTypeCfg ) ;
368
+ CompleteReferenceResolver ( interfaceTypeCfg ) ;
348
369
}
349
370
}
350
371
351
372
private void CompleteExternalFieldSetters ( ObjectType type , ObjectTypeConfiguration typeCfg )
352
373
=> ExternalSetterExpressionHelper . TryAddExternalSetter ( type , typeCfg ) ;
353
374
375
+ private void CompleteExternalFieldSetters ( InterfaceType type , InterfaceTypeConfiguration typeCfg )
376
+ => ExternalSetterExpressionHelper . TryAddExternalSetter ( type , typeCfg ) ;
377
+
354
378
private void CompleteReferenceResolver ( ObjectTypeConfiguration typeCfg )
355
379
{
356
380
var resolvers = typeCfg . Features . Get < List < ReferenceResolverConfiguration > > ( ) ;
@@ -398,6 +422,53 @@ private void CompleteReferenceResolver(ObjectTypeConfiguration typeCfg)
398
422
}
399
423
}
400
424
425
+ private void CompleteReferenceResolver ( InterfaceTypeConfiguration typeCfg )
426
+ {
427
+ var resolvers = typeCfg . Features . Get < List < ReferenceResolverConfiguration > > ( ) ;
428
+
429
+ if ( resolvers is null )
430
+ {
431
+ return ;
432
+ }
433
+
434
+ if ( resolvers . Count == 1 )
435
+ {
436
+ typeCfg . Features . Set ( new ReferenceResolver ( resolvers [ 0 ] . Resolver ) ) ;
437
+ }
438
+ else
439
+ {
440
+ var expressions = new Stack < ( Expression Condition , Expression Execute ) > ( ) ;
441
+ var context = Expression . Parameter ( typeof ( IResolverContext ) ) ;
442
+
443
+ foreach ( var resolverDef in resolvers )
444
+ {
445
+ Expression required = Expression . Constant ( resolverDef . Required ) ;
446
+ Expression resolver = Expression . Constant ( resolverDef . Resolver ) ;
447
+ Expression condition = Expression . Call ( s_matches , context , required ) ;
448
+ Expression execute = Expression . Call ( s_execute , context , resolver ) ;
449
+ expressions . Push ( ( condition , execute ) ) ;
450
+ }
451
+
452
+ Expression current = Expression . Call ( s_invalid , context ) ;
453
+ var variable = Expression . Variable ( typeof ( ValueTask < object ? > ) ) ;
454
+
455
+ while ( expressions . Count > 0 )
456
+ {
457
+ var expression = expressions . Pop ( ) ;
458
+ current = Expression . IfThenElse (
459
+ expression . Condition ,
460
+ Expression . Assign ( variable , expression . Execute ) ,
461
+ current ) ;
462
+ }
463
+
464
+ current = Expression . Block ( [ variable ] , current , variable ) ;
465
+
466
+ typeCfg . Features . Set (
467
+ new ReferenceResolver (
468
+ Expression . Lambda < FieldResolverDelegate > ( current , context ) . Compile ( ) ) ) ;
469
+ }
470
+ }
471
+
401
472
private void AddServiceTypeToQueryType (
402
473
ITypeCompletionContext completionContext ,
403
474
TypeSystemConfiguration ? definition )
@@ -452,6 +523,43 @@ private void ApplyMethodLevelReferenceResolvers(
452
523
descriptor . CreateConfiguration ( ) ;
453
524
}
454
525
526
+ private void ApplyMethodLevelReferenceResolvers (
527
+ InterfaceType interfaceType ,
528
+ InterfaceTypeConfiguration interfaceTypeCfg )
529
+ {
530
+ if ( interfaceType . RuntimeType == typeof ( object ) )
531
+ {
532
+ return ;
533
+ }
534
+
535
+ var descriptor = InterfaceTypeDescriptor . From ( _context , interfaceTypeCfg ) ;
536
+
537
+ // Static methods won't end up in the schema as fields.
538
+ // The default initialization system only considers instance methods,
539
+ // so we have to handle the attributes for those manually.
540
+ var potentiallyUnregisteredReferenceResolvers = interfaceType . RuntimeType
541
+ . GetMethods ( BindingFlags . Static | BindingFlags . Public ) ;
542
+
543
+ foreach ( var possibleReferenceResolver in potentiallyUnregisteredReferenceResolvers )
544
+ {
545
+ if ( ! possibleReferenceResolver . IsDefined ( typeof ( ReferenceResolverAttribute ) ) )
546
+ {
547
+ continue ;
548
+ }
549
+
550
+ foreach ( var attribute in possibleReferenceResolver . GetCustomAttributes ( true ) )
551
+ {
552
+ if ( attribute is ReferenceResolverAttribute casted )
553
+ {
554
+ casted . TryConfigure ( _context , descriptor , possibleReferenceResolver ) ;
555
+ }
556
+ }
557
+ }
558
+
559
+ // This seems to re-detect the entity resolver and save it into the context data.
560
+ descriptor . CreateConfiguration ( ) ;
561
+ }
562
+
455
563
private void AddToUnionIfHasTypeLevelKeyDirective (
456
564
ObjectType objectType ,
457
565
ObjectTypeConfiguration objectTypeCfg )
@@ -532,6 +640,61 @@ private void AggregatePropertyLevelKeyDirectives(
532
640
}
533
641
}
534
642
643
+ private void AggregatePropertyLevelKeyDirectives (
644
+ InterfaceTypeConfiguration interfaceTypeCfg ,
645
+ ITypeDiscoveryContext discoveryContext )
646
+ {
647
+ // if we find key markers on our fields, we need to construct the key directive
648
+ // from the annotated fields.
649
+ var foundMarkers = interfaceTypeCfg . Fields . Any ( f => f . Features . TryGet ( out KeyMarker ? _ ) ) ;
650
+
651
+ if ( ! foundMarkers )
652
+ {
653
+ return ;
654
+ }
655
+
656
+ IReadOnlyList < InterfaceFieldConfiguration > fields = interfaceTypeCfg . Fields ;
657
+ var fieldSet = new StringBuilder ( ) ;
658
+ bool ? resolvable = null ;
659
+
660
+ foreach ( var fieldDefinition in fields )
661
+ {
662
+ if ( fieldDefinition . Features . TryGet ( out KeyMarker ? key ) )
663
+ {
664
+ if ( resolvable is null )
665
+ {
666
+ resolvable = key . Resolvable ;
667
+ }
668
+ else if ( resolvable != key . Resolvable )
669
+ {
670
+ throw Key_FieldSet_ResolvableMustBeConsistent ( fieldDefinition . Member ! ) ;
671
+ }
672
+
673
+ if ( fieldSet . Length > 0 )
674
+ {
675
+ fieldSet . Append ( ' ' ) ;
676
+ }
677
+
678
+ fieldSet . Append ( fieldDefinition . Name ) ;
679
+ }
680
+ }
681
+
682
+ // add the key directive with the dynamically generated field set.
683
+ AddKeyDirective ( interfaceTypeCfg , fieldSet . ToString ( ) , resolvable ?? true ) ;
684
+
685
+ // register dependency to the key directive so that it is completed before
686
+ // we complete this type.
687
+ foreach ( var directiveDefinition in interfaceTypeCfg . Directives )
688
+ {
689
+ discoveryContext . Dependencies . Add (
690
+ new TypeDependency (
691
+ directiveDefinition . Type ,
692
+ TypeDependencyFulfilled . Completed ) ) ;
693
+
694
+ discoveryContext . Dependencies . Add ( new ( directiveDefinition . Type ) ) ;
695
+ }
696
+ }
697
+
535
698
private void AddMemberTypesToTheEntityUnionType (
536
699
ITypeCompletionContext completionContext ,
537
700
TypeSystemConfiguration ? definition )
@@ -556,4 +719,15 @@ private void AddKeyDirective(
556
719
new KeyDirective ( fieldSet , resolvable ) ,
557
720
_keyDirectiveReference ) ) ;
558
721
}
722
+
723
+ private void AddKeyDirective (
724
+ InterfaceTypeConfiguration interfaceTypeCfg ,
725
+ string fieldSet ,
726
+ bool resolvable )
727
+ {
728
+ interfaceTypeCfg . Directives . Add (
729
+ new DirectiveConfiguration (
730
+ new KeyDirective ( fieldSet , resolvable ) ,
731
+ _keyDirectiveReference ) ) ;
732
+ }
559
733
}
0 commit comments