Skip to content

Commit bcaf5ea

Browse files
committed
Support registering multiple handlers in the same class
1 parent 22b7d02 commit bcaf5ea

3 files changed

Lines changed: 250 additions & 149 deletions

File tree

src/DispatchR/Configuration/ServiceRegistrator.cs

Lines changed: 121 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -11,178 +11,169 @@ public static void RegisterHandlers(IServiceCollection services, List<Type> allT
1111
Type requestHandlerType, Type pipelineBehaviorType, Type streamRequestHandlerType,
1212
Type streamPipelineBehaviorType, bool withPipelines, List<Type>? pipelineOrder = null)
1313
{
14+
var handlerTypes = new[] { requestHandlerType, streamRequestHandlerType };
15+
var pipelineTypes = new[] { pipelineBehaviorType, streamPipelineBehaviorType };
16+
1417
var allHandlers = allTypes
15-
.Where(p =>
18+
.Where(type =>
1619
{
17-
var @interface = p.GetInterfaces().First(i => i.IsGenericType);
18-
return new[] { requestHandlerType, streamRequestHandlerType }
19-
.Contains(@interface.GetGenericTypeDefinition());
20-
}).ToList();
20+
var genericInterfaces = type.GetInterfaces()
21+
.Where(i => i.IsGenericType)
22+
.Select(i => i.GetGenericTypeDefinition())
23+
.ToList();
24+
25+
return genericInterfaces.Intersect(handlerTypes).Any() &&
26+
!genericInterfaces.Intersect(pipelineTypes).Any();
27+
})
28+
.ToList();
2129

2230
var allPipelines = allTypes
23-
.Where(p =>
24-
{
25-
var @interface = p.GetInterfaces().First(i => i.IsGenericType);
26-
return new[] { pipelineBehaviorType, streamPipelineBehaviorType }
27-
.Contains(@interface.GetGenericTypeDefinition());
28-
}).ToList();
31+
.Where(type => type.GetInterfaces()
32+
.Where(i => i.IsGenericType)
33+
.Select(i => i.GetGenericTypeDefinition())
34+
.Intersect(pipelineTypes)
35+
.Any())
36+
.ToList();
2937

3038
foreach (var handler in allHandlers)
3139
{
32-
object key = handler.GUID;
33-
var handlerType = requestHandlerType;
34-
var behaviorType = pipelineBehaviorType;
40+
var handlerInterfaces = handler.GetInterfaces()
41+
.Where(p => p.IsGenericType && handlerTypes.Contains(p.GetGenericTypeDefinition()))
42+
.ToList();
3543

36-
var isStream = handler.GetInterfaces()
37-
.Any(p => p.IsGenericType && p.GetGenericTypeDefinition() == streamRequestHandlerType);
38-
if (isStream)
44+
foreach (var handlerInterface in handlerInterfaces)
3945
{
40-
handlerType = streamRequestHandlerType;
41-
behaviorType = streamPipelineBehaviorType;
42-
}
43-
44-
services.AddKeyedScoped(typeof(IRequestHandler), key, handler);
45-
46-
var handlerInterface = handler.GetInterfaces()
47-
.First(p => p.IsGenericType && p.GetGenericTypeDefinition() == handlerType);
46+
object key = Guid.NewGuid();
47+
var handlerType = requestHandlerType;
48+
var behaviorType = pipelineBehaviorType;
4849

49-
// find pipelines
50-
if (withPipelines)
51-
{
52-
var pipelines = allPipelines
53-
.Where(p =>
54-
{
55-
var interfaces = p.GetInterfaces();
56-
if (p.IsGenericType)
57-
{
58-
// handle generic pipelines
59-
return interfaces
60-
.FirstOrDefault(inter =>
61-
inter.IsGenericType &&
62-
inter.GetGenericTypeDefinition() == behaviorType)
63-
?.GetInterfaces().First().GetGenericTypeDefinition() ==
64-
handlerInterface.GetGenericTypeDefinition();
65-
}
50+
var isStream = handlerInterface.GetGenericTypeDefinition() == streamRequestHandlerType;
51+
if (isStream)
52+
{
53+
handlerType = streamRequestHandlerType;
54+
behaviorType = streamPipelineBehaviorType;
55+
}
6656

67-
return interfaces
68-
.FirstOrDefault(inter =>
69-
inter.IsGenericType &&
70-
inter.GetGenericTypeDefinition() == behaviorType)
71-
?.GetInterfaces().First() == handlerInterface;
72-
}).ToList();
57+
services.AddKeyedScoped(typeof(IRequestHandler), key, handler);
7358

74-
// Sort pipelines by the specified order passed via ConfigurationOptions
75-
if (pipelineOrder is { Count: > 0 })
59+
// find pipelines
60+
if (withPipelines)
7661
{
77-
pipelines = pipelines
78-
.OrderBy(p =>
62+
var pipelines = allPipelines
63+
.Where(p =>
7964
{
80-
var idx = pipelineOrder.IndexOf(p);
81-
return idx == -1 ? int.MaxValue : idx;
82-
})
83-
.ToList();
84-
pipelines.Reverse();
85-
}
65+
var interfaces = p.GetInterfaces();
66+
if (p.IsGenericType)
67+
{
68+
// handle generic pipelines
69+
return interfaces
70+
.FirstOrDefault(inter =>
71+
inter.IsGenericType &&
72+
inter.GetGenericTypeDefinition() == behaviorType)
73+
?.GetInterfaces().First().GetGenericTypeDefinition() ==
74+
handlerInterface.GetGenericTypeDefinition();
75+
}
8676

87-
foreach (var pipeline in pipelines)
88-
{
89-
if (pipeline.IsGenericType)
77+
return interfaces
78+
.FirstOrDefault(inter =>
79+
inter.IsGenericType &&
80+
inter.GetGenericTypeDefinition() == behaviorType)
81+
?.GetInterfaces().First() == handlerInterface;
82+
}).ToList();
83+
84+
// Sort pipelines by the specified order passed via ConfigurationOptions
85+
if (pipelineOrder is { Count: > 0 })
9086
{
91-
var genericHandlerResponseType = pipeline.GetInterfaces().First(inter =>
92-
inter.IsGenericType &&
93-
inter.GetGenericTypeDefinition() == behaviorType).GenericTypeArguments[1];
87+
pipelines = pipelines
88+
.OrderBy(p =>
89+
{
90+
var idx = pipelineOrder.IndexOf(p);
91+
return idx == -1 ? int.MaxValue : idx;
92+
})
93+
.ToList();
94+
pipelines.Reverse();
95+
}
9496

95-
var genericHandlerResponseIsAwaitable = IsAwaitable(genericHandlerResponseType);
96-
var handlerResponseTypeIsAwaitable = IsAwaitable(handlerInterface.GenericTypeArguments[1]);
97-
if (genericHandlerResponseIsAwaitable ^ handlerResponseTypeIsAwaitable)
97+
foreach (var pipeline in pipelines)
98+
{
99+
if (pipeline.IsGenericType)
98100
{
99-
continue;
100-
}
101+
var genericHandlerResponseType = pipeline.GetInterfaces().First(inter =>
102+
inter.IsGenericType &&
103+
inter.GetGenericTypeDefinition() == behaviorType).GenericTypeArguments[1];
101104

102-
var responseTypeArg = handlerInterface.GenericTypeArguments[1];
103-
if (genericHandlerResponseIsAwaitable && handlerResponseTypeIsAwaitable)
104-
{
105-
var areGenericTypeArgumentsInHandlerInterfaceMismatched =
106-
genericHandlerResponseType.IsGenericType &&
107-
handlerInterface.GenericTypeArguments[1].IsGenericType &&
108-
genericHandlerResponseType.GetGenericTypeDefinition() !=
109-
handlerInterface.GenericTypeArguments[1].GetGenericTypeDefinition();
110-
111-
if (areGenericTypeArgumentsInHandlerInterfaceMismatched ||
112-
genericHandlerResponseType.IsGenericType ^
113-
handlerInterface.GenericTypeArguments[1].IsGenericType)
105+
var genericHandlerResponseIsAwaitable = IsAwaitable(genericHandlerResponseType);
106+
var handlerResponseTypeIsAwaitable = IsAwaitable(handlerInterface.GenericTypeArguments[1]);
107+
if (genericHandlerResponseIsAwaitable ^ handlerResponseTypeIsAwaitable)
114108
{
115109
continue;
116110
}
117111

118-
// register async generic pipelines
119-
if (responseTypeArg.GenericTypeArguments.Any())
112+
var responseTypeArg = handlerInterface.GenericTypeArguments[1];
113+
if (genericHandlerResponseIsAwaitable && handlerResponseTypeIsAwaitable)
120114
{
121-
responseTypeArg = responseTypeArg.GenericTypeArguments[0];
115+
var areGenericTypeArgumentsInHandlerInterfaceMismatched =
116+
genericHandlerResponseType.IsGenericType &&
117+
handlerInterface.GenericTypeArguments[1].IsGenericType &&
118+
genericHandlerResponseType.GetGenericTypeDefinition() !=
119+
handlerInterface.GenericTypeArguments[1].GetGenericTypeDefinition();
120+
121+
if (areGenericTypeArgumentsInHandlerInterfaceMismatched ||
122+
genericHandlerResponseType.IsGenericType ^
123+
handlerInterface.GenericTypeArguments[1].IsGenericType)
124+
{
125+
continue;
126+
}
127+
128+
// register async generic pipelines
129+
if (responseTypeArg.GenericTypeArguments.Any())
130+
{
131+
responseTypeArg = responseTypeArg.GenericTypeArguments[0];
132+
}
122133
}
123-
}
124134

125-
var closedGenericType = pipeline.MakeGenericType(handlerInterface.GenericTypeArguments[0],
126-
responseTypeArg);
127-
services.AddKeyedScoped(typeof(IRequestHandler), key, closedGenericType);
128-
}
129-
else
130-
{
131-
services.AddKeyedScoped(typeof(IRequestHandler), key, pipeline);
135+
var closedGenericType = pipeline.MakeGenericType(handlerInterface.GenericTypeArguments[0],
136+
responseTypeArg);
137+
services.AddKeyedScoped(typeof(IRequestHandler), key, closedGenericType);
138+
}
139+
else
140+
{
141+
services.AddKeyedScoped(typeof(IRequestHandler), key, pipeline);
142+
}
132143
}
133144
}
134-
}
135-
136-
services.AddScoped(handlerInterface, sp =>
137-
{
138-
var pipelinesWithHandler = Unsafe
139-
.As<IRequestHandler[]>(sp.GetKeyedServices<IRequestHandler>(key));
140145

141-
IRequestHandler lastPipeline = pipelinesWithHandler[0];
142-
for (int i = 1; i < pipelinesWithHandler.Length; i++)
146+
services.AddScoped(handlerInterface, sp =>
143147
{
144-
var pipeline = pipelinesWithHandler[i];
145-
pipeline.SetNext(lastPipeline);
146-
lastPipeline = pipeline;
147-
}
148+
var pipelinesWithHandler = Unsafe
149+
.As<IRequestHandler[]>(sp.GetKeyedServices<IRequestHandler>(key));
148150

149-
return lastPipeline;
150-
});
151+
IRequestHandler lastPipeline = pipelinesWithHandler[0];
152+
for (int i = 1; i < pipelinesWithHandler.Length; i++)
153+
{
154+
var pipeline = pipelinesWithHandler[i];
155+
pipeline.SetNext(lastPipeline);
156+
lastPipeline = pipeline;
157+
}
158+
159+
return lastPipeline;
160+
});
161+
}
151162
}
152163
}
153164

154165
public static void RegisterNotification(IServiceCollection services, List<Type> allTypes,
155166
Type syncNotificationHandlerType)
156167
{
157168
var allNotifications = allTypes
158-
.Where(p =>
159-
{
160-
return p.GetInterfaces()
161-
.Where(i => i.IsGenericType)
162-
.Select(i => i.GetGenericTypeDefinition())
163-
.Any(i => new[]
164-
{
165-
syncNotificationHandlerType
166-
}.Contains(i));
167-
})
168-
.GroupBy(p =>
169-
{
170-
var @interface = p.GetInterfaces()
171-
.Where(i => i.IsGenericType)
172-
.First(i => new[]
173-
{
174-
syncNotificationHandlerType
175-
}.Contains(i.GetGenericTypeDefinition()));
176-
return @interface.GenericTypeArguments.First();
177-
})
169+
.SelectMany(handlerType => handlerType.GetInterfaces()
170+
.Where(i => i.IsGenericType && syncNotificationHandlerType == i.GetGenericTypeDefinition())
171+
.Select(i => new { HandlerType = handlerType, Interface = i }))
178172
.ToList();
179173

180174
foreach (var notification in allNotifications)
181175
{
182-
foreach (var types in notification.ToList())
183-
{
184-
services.AddScoped(typeof(INotificationHandler<>).MakeGenericType(notification.Key), types);
185-
}
176+
services.AddScoped(notification.Interface, notification.HandlerType);
186177
}
187178
}
188179

src/DispatchR/Extensions/ServiceCollectionExtensions.cs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,24 @@ public static IServiceCollection AddDispatchR(this IServiceCollection services,
5050
var streamPipelineBehaviorType = typeof(IStreamPipelineBehavior<,>);
5151
var syncNotificationHandlerType = typeof(INotificationHandler<>);
5252

53+
var otherHandlerTypes = new HashSet<Type>()
54+
{
55+
pipelineBehaviorType,
56+
streamRequestHandlerType,
57+
streamPipelineBehaviorType,
58+
syncNotificationHandlerType
59+
};
60+
5361
var allTypes = configurationOptions.Assemblies.SelectMany(x => x.GetTypes()).Distinct()
5462
.Where(p =>
55-
{
56-
var interfaces = p.GetInterfaces();
57-
return interfaces.Length >= 1 &&
63+
p.GetInterfaces() is { Length: >= 1 } interfaces &&
5864
interfaces
5965
.Where(i => i.IsGenericType)
6066
.Select(i => i.GetGenericTypeDefinition())
61-
.Any(i =>
62-
{
63-
if (i == requestHandlerType)
64-
{
65-
return configurationOptions.IsHandlerIncluded(p);
66-
}
67-
68-
return new[]
69-
{
70-
pipelineBehaviorType,
71-
streamRequestHandlerType,
72-
streamPipelineBehaviorType,
73-
syncNotificationHandlerType
74-
}.Contains(i);
75-
});
76-
}).ToList();
67+
.Any(i => i == requestHandlerType
68+
? configurationOptions.IsHandlerIncluded(p)
69+
: otherHandlerTypes.Contains(i)))
70+
.ToList();
7771

7872
if (configurationOptions.RegisterNotifications)
7973
{

0 commit comments

Comments
 (0)