11using System . Collections ;
22using System . Collections . Concurrent ;
3+ using System . Reflection ;
4+ using Cnblogs . Architecture . Ddd . Cqrs . Abstractions . Internals ;
35using Cnblogs . Architecture . Ddd . Infrastructure . Abstractions ;
46
57namespace Cnblogs . Architecture . Ddd . Cqrs . Abstractions ;
@@ -10,7 +12,7 @@ namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;
1012public class EnricherMappingCache
1113{
1214 private readonly ConcurrentDictionary < Type , ContainerInfo > _containerInfoCache = new ( ) ;
13- private readonly ConcurrentDictionary < Type , EnricherTypeInfo > _enricherTypeInfoCache = new ( ) ;
15+ private readonly ConcurrentDictionary < Type , List < EnricherStage > > _enricherPlans = new ( ) ;
1416
1517 /// <summary>
1618 /// Get container info for a type, caching the result.
@@ -22,12 +24,104 @@ public ContainerInfo GetContainerInfo(Type type)
2224 }
2325
2426 /// <summary>
25- /// Get enricher type info for an element type, caching the result.
27+ /// Build enrich plan with given <paramref name="targetType"/> and <paramref name="enricherTypes"/>
2628 /// </summary>
27- /// <param name="elementType">The element type to resolve enrichers for.</param>
28- public EnricherTypeInfo GetEnricherTypeInfo ( Type elementType )
29+ /// <param name="targetType">The element type to enrich.</param>
30+ /// <param name="enricherTypes">Types of enrichers.</param>
31+ public void BuildEnrichPlan ( Type targetType , ICollection < Type > enricherTypes )
2932 {
30- return _enricherTypeInfoCache . GetOrAdd ( elementType , static t => new EnricherTypeInfo ( t ) ) ;
33+ // ReSharper disable once HeapView.CanAvoidClosure
34+ _enricherPlans . GetOrAdd ( targetType , _ => CompileEnricherPlan ( enricherTypes ) ) ;
35+ }
36+
37+ internal List < EnricherStage > ? GetEnricherPlan ( Type elementType )
38+ {
39+ var success = _enricherPlans . TryGetValue ( elementType , out var list ) ;
40+ return success ? list : null ;
41+ }
42+
43+ private static List < EnricherStage > CompileEnricherPlan ( ICollection < Type > enricherTypes )
44+ {
45+ if ( enricherTypes . Count == 0 )
46+ {
47+ return [ ] ;
48+ }
49+
50+ var descriptors = enricherTypes . ToDictionary ( t => t , CreateDescriptor ) ;
51+ var typeSet = enricherTypes . ToHashSet ( ) ;
52+ var inDegree = enricherTypes . ToDictionary ( x => x , _ => 0 ) ;
53+ var adjacencyList = enricherTypes . ToDictionary ( x => x , _ => new List < Type > ( ) ) ;
54+
55+ // Resolve the enricher interface for type checking
56+ var elementType = descriptors . Values . First ( ) . EnrichMethod . GetParameters ( ) [ 0 ] . ParameterType ;
57+ var enricherType = typeof ( IEnricher < > ) . MakeGenericType ( elementType ) ;
58+
59+ // Build edges from EnrichAfterAttribute
60+ foreach ( var type in enricherTypes )
61+ {
62+ var attrs = type . GetCustomAttributes < EnrichAfterAttribute > ( ) ;
63+ foreach ( var dep in attrs . SelectMany ( x => x . DependencyTypes ) . Where ( x => x . IsAssignableTo ( enricherType ) ) )
64+ {
65+ if ( typeSet . Contains ( dep ) )
66+ {
67+ // Edge: dep -> type (dep must run BEFORE type)
68+ adjacencyList [ dep ] . Add ( type ) ;
69+ inDegree [ type ] ++ ;
70+ continue ;
71+ }
72+
73+ // Dependency is not in the same group, this is a configuration error
74+ throw new InvalidOperationException (
75+ $ "Enricher '{ type } ' depends on '{ dep } ', but '{ dep } ' is not registered/resolved for this element type.") ;
76+ }
77+ }
78+
79+ return BuildDag ( enricherTypes , descriptors , inDegree , adjacencyList ) ;
80+ }
81+
82+ private static EnricherDescriptor CreateDescriptor ( Type enricherType )
83+ {
84+ var enricherInterface = enricherType . GetInterfaces ( )
85+ . First ( i => i . IsGenericType && i . GetGenericTypeDefinition ( ) == typeof ( IEnricher < > ) ) ;
86+ return new EnricherDescriptor (
87+ enricherType ,
88+ enricherInterface . GetMethod ( "EnrichAsync" ) ! ,
89+ enricherInterface . GetMethod ( "BulkEnrichAsync" ) ! ) ;
90+ }
91+
92+ private static List < EnricherStage > BuildDag (
93+ ICollection < Type > enricherTypes ,
94+ Dictionary < Type , EnricherDescriptor > descriptors ,
95+ Dictionary < Type , int > inDegree ,
96+ Dictionary < Type , List < Type > > adjacencyList )
97+ {
98+ var stages = new List < EnricherStage > ( ) ;
99+ var currentStageNodes = inDegree . Where ( x => x . Value == 0 ) . Select ( x => x . Key ) . ToList ( ) ;
100+ var processedCount = 0 ;
101+
102+ // Process level by level
103+ while ( currentStageNodes . Count > 0 )
104+ {
105+ stages . Add ( new EnricherStage ( currentStageNodes . Select ( t => descriptors [ t ] ) . ToList ( ) ) ) ;
106+ processedCount += currentStageNodes . Count ;
107+
108+ var nextStageNodes = new List < Type > ( ) ;
109+ foreach ( var dependent in currentStageNodes . SelectMany ( node => adjacencyList [ node ] ) )
110+ {
111+ inDegree [ dependent ] -- ;
112+ if ( inDegree [ dependent ] == 0 )
113+ {
114+ nextStageNodes . Add ( dependent ) ;
115+ }
116+ }
117+
118+ currentStageNodes = nextStageNodes ;
119+ }
120+
121+ // Circular dependency check
122+ return processedCount == enricherTypes . Count
123+ ? stages
124+ : throw new InvalidOperationException ( "Circular dependency detected among enrichers." ) ;
31125 }
32126
33127 private static ContainerInfo ResolveContainerInfo ( Type t )
0 commit comments