Skip to content

Commit e0b5052

Browse files
committed
feat: append to existing services (Snowberry.DependencyInjection)
1 parent ba10f62 commit e0b5052

18 files changed

+1510
-115
lines changed

src/NuGetPackage.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Platforms>AnyCPU;x64;x86</Platforms>
55

66
<Authors>Snowberry Software</Authors>
7-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
7+
<AssemblyVersion>1.0.1.0</AssemblyVersion>
88
<VersionPrefix>$(AssemblyVersion)</VersionPrefix>
99
<VersionSuffix>alpha</VersionSuffix>
1010

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Snowberry.Mediator.DependencyInjection.Shared.Contracts;
2+
3+
/// <summary>
4+
/// Defines a context for registering services with specific lifetimes.
5+
/// </summary>
6+
public interface IServiceContext
7+
{
8+
/// <summary>
9+
/// Tries to get a singleton instance of the service type <typeparamref name="T"/>.
10+
/// </summary>
11+
/// <typeparam name="T">The service type.</typeparam>
12+
/// <param name="found">Indicates whether the singleton was found.</param>
13+
/// <returns>The singleton.</returns>
14+
T? TryToGetSingleton<T>(out bool found);
15+
16+
/// <summary>
17+
/// Checks if the service type <typeparamref name="T"/> is already registered.
18+
/// </summary>
19+
/// <typeparam name="T">The service type.</typeparam>
20+
/// <returns>The result.</returns>
21+
bool IsServiceRegistered<T>();
22+
23+
/// <summary>
24+
/// Registers a service with the specified lifetime.
25+
/// </summary>
26+
/// <param name="serviceType">The service type.</param>
27+
/// <param name="implementationType">The implementation type.</param>
28+
/// <param name="lifetime">The service lifetime.</param>
29+
void Register(Type serviceType, Type implementationType, RegistrationServiceLifetime lifetime);
30+
31+
/// <summary>
32+
/// Registers a singleton instance of a service.
33+
/// </summary>
34+
/// <param name="serviceType">The service type.</param>
35+
/// <param name="instance">The instance type.</param>
36+
void Register(Type serviceType, object instance);
37+
}

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

Lines changed: 107 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,89 @@
11
using Snowberry.Mediator.Abstractions;
2+
using Snowberry.Mediator.Abstractions.Handler;
23
using Snowberry.Mediator.Abstractions.Pipeline;
4+
using Snowberry.Mediator.DependencyInjection.Shared.Contracts;
35
using Snowberry.Mediator.Extensions;
46
using Snowberry.Mediator.Models;
57
using Snowberry.Mediator.Registries;
68
using Snowberry.Mediator.Registries.Contracts;
79

810
namespace Snowberry.Mediator.DependencyInjection.Shared;
911

12+
/// <summary>
13+
/// Helper type for adding Mediator services to a service context.
14+
/// </summary>
1015
public static class DependencyInjectionHelper
1116
{
12-
public static void AddSnowberryMediator(IServiceContext serviceContext, MediatorOptions options, RegistrationServiceLifetime serviceLifetime)
17+
/// <summary>
18+
/// Adds Mediator services to the specified service context.
19+
/// </summary>
20+
/// <param name="serviceContext">The service context.</param>
21+
/// <param name="options">The options.</param>
22+
/// <param name="serviceLifetime">The service lifetime.</param>
23+
/// <param name="append">Whether to append to existing registrations or replace them.</param>
24+
public static void AddSnowberryMediator(
25+
IServiceContext serviceContext,
26+
MediatorOptions options,
27+
RegistrationServiceLifetime serviceLifetime,
28+
bool append)
1329
{
14-
serviceContext.Register(typeof(IMediator), typeof(Mediator), serviceLifetime);
15-
16-
if (options.Assemblies == null || options.Assemblies.Count == 0)
17-
return;
30+
if (!append || !serviceContext.IsServiceRegistered<IMediator>())
31+
serviceContext.Register(typeof(IMediator), typeof(Mediator), serviceLifetime);
1832

1933
var allHandlers = new List<RequestHandlerInfo>();
2034
var allStreamHandlers = new List<StreamRequestHandlerInfo>();
2135
var allPipelineBehaviorHandlers = new List<PipelineBehaviorHandlerInfo>();
2236
var allStreamPipelineBehaviorHandlers = new List<StreamPipelineBehaviorHandlerInfo>();
2337
var allNotificationHandlers = new List<NotificationHandlerInfo>();
2438

25-
for (int i = 0; i < options.Assemblies.Count; i++)
39+
if (options.Assemblies != null && options.Assemblies.Count > 0)
2640
{
27-
var assembly = options.Assemblies[i];
41+
for (int i = 0; i < options.Assemblies.Count; i++)
42+
{
43+
var assembly = options.Assemblies[i];
2844

29-
var scanResult = MediatorAssemblyHelper.ScanAssembly(assembly);
45+
var scanResult = MediatorAssemblyHelper.ScanAssembly(assembly);
3046

31-
if (scanResult.RequestHandlerTypes != null)
32-
for (int j = 0; j < scanResult.RequestHandlerTypes.Count; j++)
33-
allHandlers.Add(scanResult.RequestHandlerTypes[j]);
47+
if (scanResult.RequestHandlerTypes != null)
48+
for (int j = 0; j < scanResult.RequestHandlerTypes.Count; j++)
49+
allHandlers.Add(scanResult.RequestHandlerTypes[j]);
3450

35-
if (scanResult.StreamRequestHandlerTypes != null)
36-
for (int j = 0; j < scanResult.StreamRequestHandlerTypes.Count; j++)
37-
allStreamHandlers.Add(scanResult.StreamRequestHandlerTypes[j]);
51+
if (scanResult.StreamRequestHandlerTypes != null)
52+
for (int j = 0; j < scanResult.StreamRequestHandlerTypes.Count; j++)
53+
allStreamHandlers.Add(scanResult.StreamRequestHandlerTypes[j]);
3854

39-
if (options.RegisterPipelineBehaviors && options.ScanPipelineBehaviors && scanResult.PipelineBehaviorTypes != null)
40-
for (int j = 0; j < scanResult.PipelineBehaviorTypes.Count; j++)
41-
allPipelineBehaviorHandlers.Add(scanResult.PipelineBehaviorTypes[j]);
55+
if (options.RegisterPipelineBehaviors && options.ScanPipelineBehaviors && scanResult.PipelineBehaviorTypes != null)
56+
for (int j = 0; j < scanResult.PipelineBehaviorTypes.Count; j++)
57+
allPipelineBehaviorHandlers.Add(scanResult.PipelineBehaviorTypes[j]);
4258

43-
if (options.RegisterStreamPipelineBehaviors && options.ScanStreamPipelineBehaviors && scanResult.StreamPipelineBehaviorTypes != null)
44-
for (int j = 0; j < scanResult.StreamPipelineBehaviorTypes.Count; j++)
45-
allStreamPipelineBehaviorHandlers.Add(scanResult.StreamPipelineBehaviorTypes[j]);
59+
if (options.RegisterStreamPipelineBehaviors && options.ScanStreamPipelineBehaviors && scanResult.StreamPipelineBehaviorTypes != null)
60+
for (int j = 0; j < scanResult.StreamPipelineBehaviorTypes.Count; j++)
61+
allStreamPipelineBehaviorHandlers.Add(scanResult.StreamPipelineBehaviorTypes[j]);
4662

47-
if (options.RegisterNotificationHandlers && options.ScanNotificationHandlers && scanResult.NotificationHandlerTypes != null)
48-
for (int j = 0; j < scanResult.NotificationHandlerTypes.Count; j++)
49-
allNotificationHandlers.Add(scanResult.NotificationHandlerTypes[j]);
63+
if (options.RegisterNotificationHandlers && options.ScanNotificationHandlers && scanResult.NotificationHandlerTypes != null)
64+
for (int j = 0; j < scanResult.NotificationHandlerTypes.Count; j++)
65+
allNotificationHandlers.Add(scanResult.NotificationHandlerTypes[j]);
66+
}
5067
}
5168

5269
var pipelineBehaviorType = typeof(IPipelineBehavior<,>);
5370
var streamPipelineBehaviorType = typeof(IStreamPipelineBehavior<,>);
71+
var requestHandlerType = typeof(IRequestHandler<,>);
72+
var streamRequestHandlerType = typeof(IStreamRequestHandler<,>);
73+
74+
if (options.PipelineBehaviorTypes != null && options.RegisterPipelineBehaviors)
75+
MediatorAssemblyHelper.ParseHandlerInfo(pipelineBehaviorType, options.PipelineBehaviorTypes, allPipelineBehaviorHandlers);
76+
77+
if (options.RequestHandlerTypes != null && options.RegisterRequestHandlers)
78+
MediatorAssemblyHelper.ParseHandlerInfo(requestHandlerType, options.RequestHandlerTypes, allHandlers);
5479

55-
if (options.PipelineBehaviorTypes != null)
56-
MediatorAssemblyHelper.ParsePipelineBehaviors(pipelineBehaviorType, options.PipelineBehaviorTypes, allPipelineBehaviorHandlers);
80+
if (options.StreamRequestHandlerTypes != null && options.RegisterStreamRequestHandlers)
81+
MediatorAssemblyHelper.ParseHandlerInfo(streamRequestHandlerType, options.StreamRequestHandlerTypes, allStreamHandlers);
5782

58-
if (options.StreamPipelineBehaviorTypes != null)
59-
MediatorAssemblyHelper.ParsePipelineBehaviors(streamPipelineBehaviorType, options.StreamPipelineBehaviorTypes, allStreamPipelineBehaviorHandlers);
83+
if (options.StreamPipelineBehaviorTypes != null && options.RegisterStreamPipelineBehaviors)
84+
MediatorAssemblyHelper.ParseHandlerInfo(streamPipelineBehaviorType, options.StreamPipelineBehaviorTypes, allStreamPipelineBehaviorHandlers);
6085

61-
if (options.NotificationHandlerTypes != null)
86+
if (options.NotificationHandlerTypes != null && options.RegisterNotificationHandlers)
6287
MediatorAssemblyHelper.ParseNotificationHandlers(options.NotificationHandlerTypes, allNotificationHandlers);
6388

6489
for (int i = 0; i < allHandlers.Count; i++)
@@ -77,49 +102,91 @@ public static void AddSnowberryMediator(IServiceContext serviceContext, Mediator
77102
AddPipelineBehaviors<IGlobalPipelineRegistry, GlobalPipelineRegistry, PipelineBehaviorHandlerInfo>(
78103
serviceContext,
79104
serviceLifetime,
80-
allPipelineBehaviorHandlers);
105+
allPipelineBehaviorHandlers,
106+
append);
81107

82108
if (options.RegisterStreamPipelineBehaviors && allStreamPipelineBehaviorHandlers.Count > 0)
83109
AddPipelineBehaviors<IGlobalStreamPipelineRegistry, GlobalStreamPipelineRegistry, StreamPipelineBehaviorHandlerInfo>(
84110
serviceContext,
85111
serviceLifetime,
86-
allStreamPipelineBehaviorHandlers);
112+
allStreamPipelineBehaviorHandlers,
113+
append);
87114

88115
if (options.RegisterNotificationHandlers && allNotificationHandlers.Count > 0)
89-
AddNotificationHandlers(serviceContext, serviceLifetime, allNotificationHandlers);
116+
AddNotificationHandlers(serviceContext, serviceLifetime, allNotificationHandlers, append);
90117
}
91118

92-
private static void AddPipelineBehaviors<TGlobalPipelineInterface, TGlobalPipelineRegistry, THandlerInfo>(IServiceContext serviceContext, RegistrationServiceLifetime serviceLifetime, IList<THandlerInfo> pipelineBehaviorHandlers)
93-
where TGlobalPipelineRegistry : IBaseGlobalPipelineRegistry<THandlerInfo>, new()
119+
private static void AddPipelineBehaviors<TGlobalPipelineInterface, TGlobalPipelineRegistry, THandlerInfo>(
120+
IServiceContext serviceContext,
121+
RegistrationServiceLifetime serviceLifetime,
122+
IList<THandlerInfo> pipelineBehaviorHandlers,
123+
bool append
124+
)
125+
where TGlobalPipelineRegistry : TGlobalPipelineInterface, new()
126+
where TGlobalPipelineInterface : IBaseGlobalPipelineRegistry<THandlerInfo>
94127
where THandlerInfo : PipelineBehaviorHandlerInfo
95128
{
96129
if (pipelineBehaviorHandlers.Count == 0)
97130
return;
98131

99-
var globalPipelineRegistry = new TGlobalPipelineRegistry();
100-
serviceContext.Register(serviceType: typeof(TGlobalPipelineInterface), instance: globalPipelineRegistry);
132+
TGlobalPipelineInterface? globalPipelineRegistry = default;
133+
if (!append || !serviceContext.IsServiceRegistered<TGlobalPipelineInterface>())
134+
{
135+
globalPipelineRegistry = new TGlobalPipelineRegistry();
136+
serviceContext.Register(serviceType: typeof(TGlobalPipelineInterface), instance: globalPipelineRegistry);
137+
}
138+
else
139+
{
140+
globalPipelineRegistry = serviceContext.TryToGetSingleton<TGlobalPipelineInterface>(out bool foundSingleton);
141+
142+
if (!foundSingleton)
143+
{
144+
globalPipelineRegistry = new TGlobalPipelineRegistry();
145+
serviceContext.Register(serviceType: typeof(TGlobalPipelineInterface), instance: globalPipelineRegistry);
146+
}
147+
}
101148

102149
for (int i = 0; i < pipelineBehaviorHandlers.Count; i++)
103150
{
104151
var handler = pipelineBehaviorHandlers[i];
105-
globalPipelineRegistry.Register(handler);
152+
globalPipelineRegistry!.Register(handler);
106153

107154
serviceContext.Register(handler.HandlerType, handler.HandlerType, serviceLifetime);
108155
}
109156
}
110157

111-
private static void AddNotificationHandlers(IServiceContext serviceContext, RegistrationServiceLifetime serviceLifetime, IList<NotificationHandlerInfo> notificationHandlers)
158+
private static void AddNotificationHandlers(
159+
IServiceContext serviceContext,
160+
RegistrationServiceLifetime serviceLifetime,
161+
IList<NotificationHandlerInfo> notificationHandlers,
162+
bool append
163+
)
112164
{
113165
if (notificationHandlers.Count == 0)
114166
return;
115167

116-
var globalNotificationHandlerRegistry = new GlobalNotificationHandlerRegistry();
117-
serviceContext.Register(serviceType: typeof(IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>), instance: globalNotificationHandlerRegistry);
168+
IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>? globalNotificationHandlerRegistry = null;
169+
170+
if (!append || !serviceContext.IsServiceRegistered<IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>>())
171+
{
172+
globalNotificationHandlerRegistry = new GlobalNotificationHandlerRegistry();
173+
serviceContext.Register(serviceType: typeof(IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>), instance: globalNotificationHandlerRegistry);
174+
}
175+
else
176+
{
177+
globalNotificationHandlerRegistry = serviceContext.TryToGetSingleton<IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>>(out bool foundSingleton);
178+
179+
if (!foundSingleton)
180+
{
181+
globalNotificationHandlerRegistry = new GlobalNotificationHandlerRegistry();
182+
serviceContext.Register(serviceType: typeof(IGlobalNotificationHandlerRegistry<NotificationHandlerInfo>), instance: globalNotificationHandlerRegistry);
183+
}
184+
}
118185

119186
for (int i = 0; i < notificationHandlers.Count; i++)
120187
{
121188
var handler = notificationHandlers[i];
122-
globalNotificationHandlerRegistry.Register(handler);
189+
globalNotificationHandlerRegistry!.Register(handler);
123190

124191
serviceContext.Register(handler.HandlerType, handler.HandlerType, serviceLifetime);
125192
}

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

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
namespace Snowberry.Mediator.DependencyInjection.Shared;
22

3+
/// <summary>
4+
/// The lifetime of a service.
5+
/// </summary>
36
public enum RegistrationServiceLifetime
47
{
8+
/// <summary>
9+
/// A single instance is created and shared throughout the application's lifetime.
10+
/// </summary>
511
Singleton,
12+
13+
/// <summary>
14+
/// A new instance is created each time the service is requested.
15+
/// </summary>
616
Transient,
17+
18+
/// <summary>
19+
/// A new instance is created for each scope.
20+
/// </summary>
721
Scoped
822
}

src/Snowberry.Mediator.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,48 @@ public static class ServiceCollectionExtensions
1212
/// <summary>
1313
/// Adds the Mediator services to the specified <see cref="IServiceRegistry" />.
1414
/// </summary>
15+
/// <remarks>
16+
/// A service provider must be registered in the service collection before calling this method.
17+
/// Alternatively, if the <see cref="IServiceRegistry"/> instance is also an <see cref="IServiceProvider"/>, it will be registered as a singleton.
18+
/// </remarks>
1519
/// <param name="serviceRegistry">The service collection.</param>
1620
/// <param name="configure">The configuration method.</param>
1721
/// <param name="serviceLifetime">The service lifetime of the mediator and handlers.</param>
1822
/// <returns>The service collection.</returns>
1923
public static IServiceRegistry AddSnowberryMediator(this IServiceRegistry serviceRegistry, Action<MediatorOptions> configure, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
24+
{
25+
return AddSnowberryMediator(serviceRegistry, configure, serviceLifetime, append: false);
26+
}
27+
28+
/// <summary>
29+
/// Appends the Mediator services to the specified <see cref="IServiceRegistry" /> and preserves existing registrations.
30+
/// </summary>
31+
/// <remarks>
32+
/// A service provider must be registered in the service collection before calling this method.
33+
/// Alternatively, if the <see cref="IServiceRegistry"/> instance is also an <see cref="IServiceProvider"/>, it will be registered as a singleton.
34+
/// </remarks>
35+
/// <param name="serviceRegistry">The service collection.</param>
36+
/// <param name="configure">The configuration method.</param>
37+
/// <param name="serviceLifetime">The service lifetime of the mediator and handlers.</param>
38+
/// <returns>The service collection.</returns>
39+
public static IServiceRegistry AppendSnowberryMediator(this IServiceRegistry serviceRegistry, Action<MediatorOptions> configure, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
40+
{
41+
return AddSnowberryMediator(serviceRegistry, configure, serviceLifetime, append: true);
42+
}
43+
44+
/// <summary>
45+
/// Adds the Mediator services to the specified <see cref="IServiceRegistry" />.
46+
/// </summary>
47+
/// <remarks>
48+
/// A service provider must be registered in the service collection before calling this method.
49+
/// Alternatively, if the <see cref="IServiceRegistry"/> instance is also an <see cref="IServiceProvider"/>, it will be registered as a singleton.
50+
/// </remarks>
51+
/// <param name="serviceRegistry">The service collection.</param>
52+
/// <param name="configure">The configuration method.</param>
53+
/// <param name="serviceLifetime">The service lifetime of the mediator and handlers.</param>
54+
/// <param name="append">Whether to append the registrations to existing ones.</param>
55+
/// <returns>The service collection.</returns>
56+
private static IServiceRegistry AddSnowberryMediator(this IServiceRegistry serviceRegistry, Action<MediatorOptions> configure, ServiceLifetime serviceLifetime, bool append)
2057
{
2158
var options = new MediatorOptions();
2259
configure(options);
@@ -28,7 +65,7 @@ public static IServiceRegistry AddSnowberryMediator(this IServiceRegistry servic
2865
ServiceLifetime.Singleton => RegistrationServiceLifetime.Singleton,
2966
ServiceLifetime.Transient => RegistrationServiceLifetime.Transient,
3067
_ => throw new NotSupportedException($"The service lifetime '{serviceLifetime}' is not supported."),
31-
});
68+
}, append: append);
3269

3370
if (!serviceRegistry.IsServiceRegistered<IServiceProvider>(serviceKey: null))
3471
{

0 commit comments

Comments
 (0)