@@ -18,6 +18,8 @@ namespace Devlooped.Extensions.DependencyInjection.Attributed;
1818[ Generator ( LanguageNames . CSharp ) ]
1919public class IncrementalGenerator : IIncrementalGenerator
2020{
21+ record ServiceSymbol ( INamedTypeSymbol Type , TypedConstant ? Key , int Lifetime ) ;
22+
2123 public void Initialize ( IncrementalGeneratorInitializationContext context )
2224 {
2325 var types = context . CompilationProvider . SelectMany ( ( x , c ) =>
@@ -58,65 +60,65 @@ bool IsExport(AttributeData attr)
5860 // NOTE: we recognize the attribute by name, not precise type. This makes the generator
5961 // more flexible and avoids requiring any sort of run-time dependency.
6062 var services = types
61- . Select ( ( x , _ ) =>
63+ . SelectMany ( ( x , _ ) =>
6264 {
6365 var name = x . Name ;
6466 var attrs = x . GetAttributes ( ) ;
65- var serviceAttr = attrs . FirstOrDefault ( IsService ) ?? attrs . FirstOrDefault ( IsKeyedService ) ;
66- var service = serviceAttr != null || attrs . Any ( IsExport ) ;
67+ var services = new List < ServiceSymbol > ( ) ;
6768
68- if ( ! service )
69- return null ;
69+ foreach ( var attr in attrs )
70+ {
71+ var serviceAttr = IsService ( attr ) || IsKeyedService ( attr ) ? attr : null ;
72+ if ( serviceAttr == null && ! IsExport ( attr ) )
73+ continue ;
7074
71- TypedConstant ? key = default ;
75+ TypedConstant ? key = default ;
7276
73- // Default lifetime is singleton for [Service], Transient for MEF
74- var lifetime = serviceAttr != null ? 0 : 2 ;
75- if ( serviceAttr != null )
76- {
77- if ( IsKeyedService ( serviceAttr ) )
77+ // Default lifetime is singleton for [Service], Transient for MEF
78+ var lifetime = serviceAttr != null ? 0 : 2 ;
79+ if ( serviceAttr != null )
7880 {
79- key = serviceAttr . ConstructorArguments [ 0 ] ;
80- lifetime = ( int ) serviceAttr . ConstructorArguments [ 1 ] . Value ! ;
81+ if ( IsKeyedService ( serviceAttr ) )
82+ {
83+ key = serviceAttr . ConstructorArguments [ 0 ] ;
84+ lifetime = ( int ) serviceAttr . ConstructorArguments [ 1 ] . Value ! ;
85+ }
86+ else
87+ {
88+ lifetime = ( int ) serviceAttr . ConstructorArguments [ 0 ] . Value ! ;
89+ }
8190 }
8291 else
8392 {
84- lifetime = ( int ) serviceAttr . ConstructorArguments [ 0 ] . Value ! ;
85- }
86- }
87- else
88- {
89- // In NuGet MEF, [Shared] makes exports singleton
90- if ( attrs . Any ( a => a . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.Composition.SharedAttribute" ) )
91- {
92- lifetime = 0 ;
93- }
94- // In .NET MEF, [PartCreationPolicy(CreationPolicy.Shared)] does it.
95- else if ( attrs . Any ( a =>
96- a . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.ComponentModel.Composition.PartCreationPolicyAttribute" &&
97- a . ConstructorArguments . Length == 1 &&
98- a . ConstructorArguments [ 0 ] . Kind == TypedConstantKind . Enum &&
99- a . ConstructorArguments [ 0 ] . Type ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.ComponentModel.Composition.CreationPolicy" &&
100- ( int ) a . ConstructorArguments [ 0 ] . Value ! == 1 ) )
101- {
102- lifetime = 0 ;
103- }
93+ // In NuGet MEF, [Shared] makes exports singleton
94+ if ( attrs . Any ( a => a . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.Composition.SharedAttribute" ) )
95+ {
96+ lifetime = 0 ;
97+ }
98+ // In .NET MEF, [PartCreationPolicy(CreationPolicy.Shared)] does it.
99+ else if ( attrs . Any ( a =>
100+ a . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.ComponentModel.Composition.PartCreationPolicyAttribute" &&
101+ a . ConstructorArguments . Length == 1 &&
102+ a . ConstructorArguments [ 0 ] . Kind == TypedConstantKind . Enum &&
103+ a . ConstructorArguments [ 0 ] . Type ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == "global::System.ComponentModel.Composition.CreationPolicy" &&
104+ ( int ) a . ConstructorArguments [ 0 ] . Value ! == 1 ) )
105+ {
106+ lifetime = 0 ;
107+ }
104108
105- // Consider the [Export(contractName)] as a keyed service with the contract name as the key.
106- if ( attrs . FirstOrDefault ( IsExport ) is { } export &&
107- export . ConstructorArguments . Length > 0 &&
108- export . ConstructorArguments [ 0 ] . Kind == TypedConstantKind . Primitive )
109- {
110- key = export . ConstructorArguments [ 0 ] ;
109+ // Consider the [Export(contractName)] as a keyed service with the contract name as the key.
110+ if ( attrs . FirstOrDefault ( IsExport ) is { } export &&
111+ export . ConstructorArguments . Length > 0 &&
112+ export . ConstructorArguments [ 0 ] . Kind == TypedConstantKind . Primitive )
113+ {
114+ key = export . ConstructorArguments [ 0 ] ;
115+ }
111116 }
117+
118+ services . Add ( new ( x , key , lifetime ) ) ;
112119 }
113120
114- return new
115- {
116- Type = x ,
117- Key = key ,
118- Lifetime = lifetime
119- } ;
121+ return services . ToImmutableArray ( ) ;
120122 } )
121123 . Where ( x => x != null ) ;
122124
0 commit comments