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