Skip to content

Commit a820141

Browse files
committed
feat: enhance service registration with custom callback and improve handler collections
1 parent fae8f0e commit a820141

File tree

7 files changed

+609
-74
lines changed

7 files changed

+609
-74
lines changed

src/Snowberry.Mediator.DependencyInjection.Shared/Contracts/IServiceContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public interface IServiceContext
2828
/// <param name="serviceType">The service type.</param>
2929
/// <param name="implementationType">The implementation type.</param>
3030
/// <param name="lifetime">The service lifetime.</param>
31-
void TryRegister(Type serviceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType, RegistrationServiceLifetime lifetime);
31+
void TryRegister(Type serviceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType, RegistrationServiceLifetime lifetime);
3232

3333
/// <summary>
3434
/// Registers a singleton instance of a service.

src/Snowberry.Mediator.DependencyInjection.Shared/DependencyInjectionHelper.cs

Lines changed: 97 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics.CodeAnalysis;
2+
using System.Reflection;
23
using Snowberry.Mediator.Abstractions;
34
using Snowberry.Mediator.Abstractions.Handler;
45
using Snowberry.Mediator.Abstractions.Pipeline;
@@ -15,108 +16,143 @@ namespace Snowberry.Mediator.DependencyInjection.Shared;
1516
/// </summary>
1617
public static class DependencyInjectionHelper
1718
{
19+
public delegate void CustomAddCallbackDelegate(
20+
IServiceContext serviceContext,
21+
MediatorOptions options,
22+
RegistrationServiceLifetime serviceLifetime,
23+
HandlerCollection handlerCollection,
24+
bool append);
25+
1826
/// <summary>
1927
/// Adds Mediator services to the specified service context.
2028
/// </summary>
29+
/// <remarks>This variant ignores the <see cref="MediatorOptions.Assemblies"/> option to be more compatible with AOT scenarios.</remarks>
2130
/// <param name="serviceContext">The service context.</param>
2231
/// <param name="options">The options.</param>
2332
/// <param name="serviceLifetime">The service lifetime.</param>
2433
/// <param name="append">Whether to append to existing registrations or replace them.</param>
25-
[RequiresUnreferencedCode("Assembly scanning requires unreferenced code. Use explicit handler registration for AOT compatibility.")]
34+
/// <param name="customCallback">A custom callback to execute at the start during registration.</param>
2635
[RequiresDynamicCode("Creating generic handler types at runtime requires dynamic code. Use explicit handler registration for AOT compatibility.")]
27-
public static void AddSnowberryMediator(
36+
public static void AddSnowberryMediatorNoScan(
2837
IServiceContext serviceContext,
2938
MediatorOptions options,
3039
RegistrationServiceLifetime serviceLifetime,
31-
bool append)
40+
bool append,
41+
CustomAddCallbackDelegate? customCallback)
3242
{
3343
if (!append || !serviceContext.IsServiceRegistered<IMediator>())
3444
serviceContext.TryRegister(typeof(IMediator), typeof(Mediator), serviceLifetime);
3545

36-
var allHandlers = new List<RequestHandlerInfo>();
37-
var allStreamHandlers = new List<StreamRequestHandlerInfo>();
38-
var allPipelineBehaviorHandlers = new List<PipelineBehaviorHandlerInfo>();
39-
var allStreamPipelineBehaviorHandlers = new List<StreamPipelineBehaviorHandlerInfo>();
40-
var allNotificationHandlers = new List<NotificationHandlerInfo>();
41-
42-
if (options.Assemblies != null && options.Assemblies.Count > 0)
43-
{
44-
for (int i = 0; i < options.Assemblies.Count; i++)
45-
{
46-
var assembly = options.Assemblies[i];
47-
48-
var scanResult = MediatorAssemblyHelper.ScanAssembly(assembly);
49-
50-
if (scanResult.RequestHandlerTypes != null)
51-
for (int j = 0; j < scanResult.RequestHandlerTypes.Count; j++)
52-
allHandlers.Add(scanResult.RequestHandlerTypes[j]);
53-
54-
if (scanResult.StreamRequestHandlerTypes != null)
55-
for (int j = 0; j < scanResult.StreamRequestHandlerTypes.Count; j++)
56-
allStreamHandlers.Add(scanResult.StreamRequestHandlerTypes[j]);
46+
var handlerCollection = new HandlerCollection();
5747

58-
if (options.RegisterPipelineBehaviors && options.ScanPipelineBehaviors && scanResult.PipelineBehaviorTypes != null)
59-
for (int j = 0; j < scanResult.PipelineBehaviorTypes.Count; j++)
60-
allPipelineBehaviorHandlers.Add(scanResult.PipelineBehaviorTypes[j]);
61-
62-
if (options.RegisterStreamPipelineBehaviors && options.ScanStreamPipelineBehaviors && scanResult.StreamPipelineBehaviorTypes != null)
63-
for (int j = 0; j < scanResult.StreamPipelineBehaviorTypes.Count; j++)
64-
allStreamPipelineBehaviorHandlers.Add(scanResult.StreamPipelineBehaviorTypes[j]);
65-
66-
if (options.RegisterNotificationHandlers && options.ScanNotificationHandlers && scanResult.NotificationHandlerTypes != null)
67-
for (int j = 0; j < scanResult.NotificationHandlerTypes.Count; j++)
68-
allNotificationHandlers.Add(scanResult.NotificationHandlerTypes[j]);
69-
}
70-
}
48+
customCallback?.Invoke(serviceContext, options, serviceLifetime, handlerCollection, append);
7149

7250
var pipelineBehaviorType = typeof(IPipelineBehavior<,>);
7351
var streamPipelineBehaviorType = typeof(IStreamPipelineBehavior<,>);
7452
var requestHandlerType = typeof(IRequestHandler<,>);
7553
var streamRequestHandlerType = typeof(IStreamRequestHandler<,>);
7654

7755
if (options.PipelineBehaviorTypes != null && options.RegisterPipelineBehaviors)
78-
MediatorAssemblyHelper.ParseHandlerInfo(pipelineBehaviorType, options.PipelineBehaviorTypes, allPipelineBehaviorHandlers);
56+
MediatorAssemblyHelper.ParseHandlerInfo(pipelineBehaviorType, options.PipelineBehaviorTypes, handlerCollection.AllPipelineBehaviorHandlers);
7957

8058
if (options.RequestHandlerTypes != null && options.RegisterRequestHandlers)
81-
MediatorAssemblyHelper.ParseHandlerInfo(requestHandlerType, options.RequestHandlerTypes, allHandlers);
59+
MediatorAssemblyHelper.ParseHandlerInfo(requestHandlerType, options.RequestHandlerTypes, handlerCollection.AllHandlers);
8260

8361
if (options.StreamRequestHandlerTypes != null && options.RegisterStreamRequestHandlers)
84-
MediatorAssemblyHelper.ParseHandlerInfo(streamRequestHandlerType, options.StreamRequestHandlerTypes, allStreamHandlers);
62+
MediatorAssemblyHelper.ParseHandlerInfo(streamRequestHandlerType, options.StreamRequestHandlerTypes, handlerCollection.AllStreamHandlers);
8563

8664
if (options.StreamPipelineBehaviorTypes != null && options.RegisterStreamPipelineBehaviors)
87-
MediatorAssemblyHelper.ParseHandlerInfo(streamPipelineBehaviorType, options.StreamPipelineBehaviorTypes, allStreamPipelineBehaviorHandlers);
65+
MediatorAssemblyHelper.ParseHandlerInfo(streamPipelineBehaviorType, options.StreamPipelineBehaviorTypes, handlerCollection.AllStreamPipelineBehaviorHandlers);
8866

8967
if (options.NotificationHandlerTypes != null && options.RegisterNotificationHandlers)
90-
MediatorAssemblyHelper.ParseNotificationHandlers(options.NotificationHandlerTypes, allNotificationHandlers);
68+
MediatorAssemblyHelper.ParseNotificationHandlers(options.NotificationHandlerTypes, handlerCollection.AllNotificationHandlers);
9169

92-
for (int i = 0; i < allHandlers.Count; i++)
70+
for (int i = 0; i < handlerCollection.AllHandlers.Count; i++)
9371
{
94-
var handlerInfo = allHandlers[i];
72+
var handlerInfo = handlerCollection.AllHandlers[i];
9573
serviceContext.TryRegister(handlerInfo.CreateRequestHandlerInterfaceType(), handlerInfo.HandlerType, serviceLifetime);
9674
}
9775

98-
for (int i = 0; i < allStreamHandlers.Count; i++)
76+
for (int i = 0; i < handlerCollection.AllStreamHandlers.Count; i++)
9977
{
100-
var handlerInfo = allStreamHandlers[i];
78+
var handlerInfo = handlerCollection.AllStreamHandlers[i];
10179
serviceContext.TryRegister(handlerInfo.CreateStreamRequestHandlerInterfaceType(), handlerInfo.HandlerType, serviceLifetime);
10280
}
10381

104-
if (options.RegisterPipelineBehaviors && allPipelineBehaviorHandlers.Count > 0)
82+
if (options.RegisterPipelineBehaviors && handlerCollection.AllPipelineBehaviorHandlers.Count > 0)
10583
AddPipelineBehaviors<IGlobalPipelineRegistry, GlobalPipelineRegistry, PipelineBehaviorHandlerInfo>(
10684
serviceContext,
10785
serviceLifetime,
108-
allPipelineBehaviorHandlers,
86+
handlerCollection.AllPipelineBehaviorHandlers,
10987
append);
11088

111-
if (options.RegisterStreamPipelineBehaviors && allStreamPipelineBehaviorHandlers.Count > 0)
89+
if (options.RegisterStreamPipelineBehaviors && handlerCollection.AllStreamPipelineBehaviorHandlers.Count > 0)
11290
AddPipelineBehaviors<IGlobalStreamPipelineRegistry, GlobalStreamPipelineRegistry, StreamPipelineBehaviorHandlerInfo>(
11391
serviceContext,
11492
serviceLifetime,
115-
allStreamPipelineBehaviorHandlers,
93+
handlerCollection.AllStreamPipelineBehaviorHandlers,
11694
append);
11795

118-
if (options.RegisterNotificationHandlers && allNotificationHandlers.Count > 0)
119-
AddNotificationHandlers(serviceContext, serviceLifetime, allNotificationHandlers, append);
96+
if (options.RegisterNotificationHandlers && handlerCollection.AllNotificationHandlers.Count > 0)
97+
AddNotificationHandlers(serviceContext, serviceLifetime, handlerCollection.AllNotificationHandlers, append);
98+
}
99+
100+
/// <summary>
101+
/// Adds Mediator services to the specified service context.
102+
/// </summary>
103+
/// <param name="serviceContext">The service context.</param>
104+
/// <param name="options">The options.</param>
105+
/// <param name="serviceLifetime">The service lifetime.</param>
106+
/// <param name="append">Whether to append to existing registrations or replace them.</param>
107+
/// <param name="customCallback">A custom callback to execute at the start during registration.</param>
108+
[RequiresUnreferencedCode("Assembly scanning requires unreferenced code. Use explicit handler registration for AOT compatibility.")]
109+
[RequiresDynamicCode("Creating generic handler types at runtime requires dynamic code. Use explicit handler registration for AOT compatibility.")]
110+
public static void AddSnowberryMediator(
111+
IServiceContext serviceContext,
112+
MediatorOptions options,
113+
RegistrationServiceLifetime serviceLifetime,
114+
bool append,
115+
CustomAddCallbackDelegate? customCallback = null)
116+
{
117+
AddSnowberryMediatorNoScan(serviceContext, options, serviceLifetime, append, (_, _, _, handlerCollection, _) =>
118+
{
119+
if (options.Assemblies != null && options.Assemblies.Count > 0)
120+
{
121+
for (int i = 0; i < options.Assemblies.Count; i++)
122+
{
123+
var assembly = options.Assemblies[i];
124+
ScanAssembly(options, handlerCollection, assembly);
125+
}
126+
}
127+
128+
customCallback?.Invoke(serviceContext, options, serviceLifetime, handlerCollection, append);
129+
});
130+
}
131+
132+
[RequiresUnreferencedCode("Assembly scanning requires unreferenced code. Use explicit handler registration for AOT compatibility.")]
133+
public static void ScanAssembly(MediatorOptions options, HandlerCollection handlerCollection, Assembly assembly)
134+
{
135+
var scanResult = MediatorAssemblyHelper.ScanAssembly(assembly);
136+
137+
if (scanResult.RequestHandlerTypes != null)
138+
for (int j = 0; j < scanResult.RequestHandlerTypes.Count; j++)
139+
handlerCollection.AllHandlers.Add(scanResult.RequestHandlerTypes[j]);
140+
141+
if (scanResult.StreamRequestHandlerTypes != null)
142+
for (int j = 0; j < scanResult.StreamRequestHandlerTypes.Count; j++)
143+
handlerCollection.AllStreamHandlers.Add(scanResult.StreamRequestHandlerTypes[j]);
144+
145+
if (options.RegisterPipelineBehaviors && options.ScanPipelineBehaviors && scanResult.PipelineBehaviorTypes != null)
146+
for (int j = 0; j < scanResult.PipelineBehaviorTypes.Count; j++)
147+
handlerCollection.AllPipelineBehaviorHandlers.Add(scanResult.PipelineBehaviorTypes[j]);
148+
149+
if (options.RegisterStreamPipelineBehaviors && options.ScanStreamPipelineBehaviors && scanResult.StreamPipelineBehaviorTypes != null)
150+
for (int j = 0; j < scanResult.StreamPipelineBehaviorTypes.Count; j++)
151+
handlerCollection.AllStreamPipelineBehaviorHandlers.Add(scanResult.StreamPipelineBehaviorTypes[j]);
152+
153+
if (options.RegisterNotificationHandlers && options.ScanNotificationHandlers && scanResult.NotificationHandlerTypes != null)
154+
for (int j = 0; j < scanResult.NotificationHandlerTypes.Count; j++)
155+
handlerCollection.AllNotificationHandlers.Add(scanResult.NotificationHandlerTypes[j]);
120156
}
121157

122158
private static void AddPipelineBehaviors<TGlobalPipelineInterface, TGlobalPipelineRegistry, THandlerInfo>(
@@ -194,4 +230,13 @@ bool append
194230
serviceContext.TryRegister(handler.HandlerType, handler.HandlerType, serviceLifetime);
195231
}
196232
}
233+
234+
public class HandlerCollection
235+
{
236+
public readonly List<RequestHandlerInfo> AllHandlers = [];
237+
public readonly List<StreamRequestHandlerInfo> AllStreamHandlers = [];
238+
public readonly List<PipelineBehaviorHandlerInfo> AllPipelineBehaviorHandlers = [];
239+
public readonly List<StreamPipelineBehaviorHandlerInfo> AllStreamPipelineBehaviorHandlers = [];
240+
public readonly List<NotificationHandlerInfo> AllNotificationHandlers = [];
241+
}
197242
}

0 commit comments

Comments
 (0)