Skip to content

Commit 450a41f

Browse files
committed
fix: void request handlers
1 parent 51bbd9e commit 450a41f

20 files changed

Lines changed: 367 additions & 32 deletions

src/Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22

33
<PropertyGroup>
4-
<Version>1.0.0-alpha.17</Version>
5-
<PackageVersion>1.0.0-alpha.17</PackageVersion>
4+
<Version>1.0.0-alpha.18</Version>
5+
<PackageVersion>1.0.0-alpha.18</PackageVersion>
66
<Authors>Zapto</Authors>
77
<RepositoryUrl>https://github.com/zapto-dev/Mediator</RepositoryUrl>
88
<Copyright>Copyright © 2025 Zapto</Copyright>

src/Mediator.DependencyInjection/Builder/MediatorBuilder.Helpers.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public partial class MediatorBuilder
1717

1818
private void RegisterHandler(
1919
string registerMethodName,
20-
Type parameterTypeTarget,
20+
Type[] parameterTypeTargets,
2121
string noResultMessage,
2222
string multipleResultMessage,
2323
Delegate handler)
@@ -27,12 +27,13 @@ private void RegisterHandler(
2727
.GetParameters()
2828
.Select(i =>
2929
{
30-
var type = i.ParameterType.GetInterfaces().FirstOrDefault(t =>
31-
parameterTypeTarget.IsGenericType == t.IsGenericType && (
32-
t.IsGenericType
33-
? t.GetGenericTypeDefinition() == parameterTypeTarget
34-
: t == parameterTypeTarget
35-
));
30+
var type = i.ParameterType.GetInterfaces().FirstOrDefault(t1 =>
31+
parameterTypeTargets.Any(t2 =>
32+
t2.IsGenericType == t1.IsGenericType && (
33+
t1.IsGenericType
34+
? t1.GetGenericTypeDefinition() == t2
35+
: t1 == t2
36+
)));
3637

3738
return (Type: type!, Parameter: i);
3839
})
@@ -168,6 +169,7 @@ private void RegisterHandler(
168169
var registerMethod = typeof(MediatorBuilder).GetMethods()
169170
.First(i =>
170171
i.Name == registerMethodName && i.IsGenericMethod &&
172+
i.GetGenericArguments().Length == (interfaceType.IsGenericType ? 1 + interfaceType.GetGenericArguments().Length : 1) &&
171173
i.GetParameters().FirstOrDefault() is {ParameterType: {IsGenericType: true} p} &&
172174
p.GetGenericTypeDefinition() == typeof(Func<,,,>));
173175

src/Mediator.DependencyInjection/Builder/MediatorBuilder.NotificationHandler.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,16 @@ public IMediatorBuilder AddNotificationHandler<TNotification>(Func<IServiceProvi
6464
return this;
6565
}
6666

67+
private static readonly Type[] NotificationParameterTypeTargets = new[]
68+
{
69+
typeof(INotification),
70+
};
71+
6772
public IMediatorBuilder AddNotificationHandler(Delegate handler)
6873
{
6974
RegisterHandler(
7075
registerMethodName: nameof(AddNotificationHandler),
71-
parameterTypeTarget: typeof(INotification),
76+
parameterTypeTargets: NotificationParameterTypeTargets,
7277
noResultMessage: "No notification found in delegate",
7378
multipleResultMessage: "Multiple notifications found in delegate",
7479
handler: handler);

src/Mediator.DependencyInjection/Builder/MediatorBuilder.RequestHandler.cs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,36 @@ public IMediatorBuilder AddRequestHandler<TRequest, TResponse, THandler>(Registr
2727
}
2828

2929
public IMediatorBuilder AddRequestHandler<TRequest, THandler>(RegistrationScope scope = RegistrationScope.Transient)
30-
where TRequest : IRequest<Unit>
31-
where THandler : class, IRequestHandler<TRequest, Unit>
30+
where TRequest : IRequest
31+
where THandler : class, IRequestHandler<TRequest>
3232
{
33-
return AddRequestHandler<TRequest, Unit, THandler>(scope);
33+
if (_ns == null)
34+
{
35+
_services.Add(new ServiceDescriptor(typeof(IRequestHandler<TRequest>), typeof(THandler), GetLifetime(scope)));
36+
}
37+
else
38+
{
39+
_services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), GetLifetime(scope)));
40+
_services.AddSingleton<INamespaceRequestHandler<TRequest>>(new NamespaceRequestHandlerProvider<TRequest, THandler>(_ns.Value));
41+
}
42+
43+
return this;
44+
}
45+
46+
public IMediatorBuilder AddRequestHandler<TRequest>(IRequestHandler<TRequest> handler) where TRequest : IRequest
47+
{
48+
if (_ns == null)
49+
{
50+
_services.AddSingleton(handler);
51+
}
52+
else
53+
{
54+
_services.AddSingleton<INamespaceRequestHandler<TRequest>>(
55+
new NamespaceRequestHandler<TRequest>(_ns.Value, handler)
56+
);
57+
}
58+
59+
return this;
3460
}
3561

3662
public IMediatorBuilder AddRequestHandler<TRequest, TResponse>(IRequestHandler<TRequest, TResponse> handler)
@@ -50,6 +76,23 @@ public IMediatorBuilder AddRequestHandler<TRequest, TResponse>(IRequestHandler<T
5076
return this;
5177
}
5278

79+
public IMediatorBuilder AddRequestHandler<TRequest>(Func<IServiceProvider, TRequest, CancellationToken, ValueTask> handler) where TRequest : IRequest
80+
{
81+
if (_ns == null)
82+
{
83+
_services.AddSingleton<IRequestHandler<TRequest>>(
84+
new FuncRequestHandler<TRequest>(handler));
85+
}
86+
else
87+
{
88+
_services.AddSingleton<INamespaceRequestHandler<TRequest>>(
89+
new NamespaceRequestHandler<TRequest>(_ns.Value,
90+
new FuncRequestHandler<TRequest>(handler)));
91+
}
92+
93+
return this;
94+
}
95+
5396
public IMediatorBuilder AddRequestHandler<TRequest, TResponse>(Func<IServiceProvider, TRequest, CancellationToken, ValueTask<TResponse>> handler)
5497
where TRequest : IRequest<TResponse>
5598
{
@@ -68,11 +111,17 @@ public IMediatorBuilder AddRequestHandler<TRequest, TResponse>(Func<IServiceProv
68111
return this;
69112
}
70113

114+
private static readonly Type[] RequestParameterTypeTargets = new[]
115+
{
116+
typeof(IRequest<>),
117+
typeof(IRequest)
118+
};
119+
71120
public IMediatorBuilder AddRequestHandler(Delegate handler)
72121
{
73122
RegisterHandler(
74123
registerMethodName: nameof(AddRequestHandler),
75-
parameterTypeTarget: typeof(IRequest<>),
124+
parameterTypeTargets: RequestParameterTypeTargets,
76125
noResultMessage: "No request found in delegate",
77126
multipleResultMessage: "Multiple requests found in delegate",
78127
handler: handler);

src/Mediator.DependencyInjection/Extensions/ServiceExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public static IMediatorBuilder AddMediator(this IServiceCollection services)
2020
services.TryAddSingleton(typeof(GenericRequestCache<,>));
2121
services.TryAddTransient(typeof(IRequestHandler<,>), typeof(GenericRequestHandler<,>));
2222

23+
services.TryAddSingleton(typeof(GenericRequestCache<>));
24+
services.TryAddTransient(typeof(IRequestHandler<>), typeof(GenericRequestHandler<>));
25+
2326
services.TryAddSingleton(typeof(GenericNotificationCache<>));
2427
services.TryAddTransient(typeof(GenericNotificationHandler<>));
2528

src/Mediator.DependencyInjection/Generic/Handlers/GenericRequestHandler.cs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4+
using System.Linq;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using MediatR;
@@ -29,6 +30,11 @@ internal sealed class GenericRequestCache<TRequest, TResponse>
2930
public Type? RequestHandlerType { get; set; }
3031
}
3132

33+
internal sealed class GenericRequestCache<TRequest>
34+
{
35+
public Type? RequestHandlerType { get; set; }
36+
}
37+
3238
internal sealed class GenericRequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
3339
where TRequest : IRequest<TResponse>
3440
{
@@ -103,10 +109,90 @@ public async ValueTask<TResponse> Handle(IServiceProvider provider, TRequest req
103109

104110
var method = _defaultHandler
105111
.GetType()
106-
.GetMethod(nameof(IDefaultRequestHandler.Handle))!
112+
.GetMethods()
113+
.First(m => m.Name == nameof(IDefaultRequestHandler.Handle) && m.GetGenericArguments().Length == 2)
107114
.MakeGenericMethod(arguments);
108115

109116
return await (ValueTask<TResponse>) method.Invoke(_defaultHandler, new object[] {_serviceProvider, request, ct})!;
110-
111117
}
112118
}
119+
120+
121+
internal sealed class GenericRequestHandler<TRequest> : IRequestHandler<TRequest>
122+
where TRequest : IRequest
123+
{
124+
private readonly GenericRequestCache<TRequest> _cache;
125+
private readonly IEnumerable<GenericRequestRegistration> _enumerable;
126+
private readonly IServiceProvider _serviceProvider;
127+
private readonly IDefaultRequestHandler? _defaultHandler;
128+
129+
public GenericRequestHandler(
130+
IEnumerable<GenericRequestRegistration> enumerable,
131+
IServiceProvider serviceProvider,
132+
GenericRequestCache<TRequest> cache,
133+
IDefaultRequestHandler? defaultHandler = null)
134+
{
135+
_enumerable = enumerable;
136+
_serviceProvider = serviceProvider;
137+
_cache = cache;
138+
_defaultHandler = defaultHandler;
139+
}
140+
141+
public async ValueTask Handle(IServiceProvider provider, TRequest request, CancellationToken ct)
142+
{
143+
if (_cache.RequestHandlerType is {} cachedType)
144+
{
145+
var handler = _serviceProvider.GetRequiredService(cachedType);
146+
147+
await ((IRequestHandler<TRequest>)handler).Handle(provider, request, ct);
148+
return;
149+
}
150+
151+
var requestType = typeof(TRequest);
152+
153+
if (!requestType.IsGenericType)
154+
{
155+
if (_defaultHandler is null)
156+
{
157+
throw new HandlerNotFoundException($"No handler found for request type {requestType.FullName}.");
158+
}
159+
160+
await _defaultHandler.Handle(_serviceProvider, request, ct);
161+
return;
162+
}
163+
164+
var arguments = requestType.GetGenericArguments();
165+
166+
requestType = requestType.GetGenericTypeDefinition();
167+
168+
foreach (var registration in _enumerable)
169+
{
170+
if (registration.RequestType != requestType ||
171+
registration.ResponseType != null)
172+
{
173+
continue;
174+
}
175+
176+
var type = registration.HandlerType.MakeGenericType(arguments);
177+
var handler = (IRequestHandler<TRequest>) _serviceProvider.GetRequiredService(type);
178+
179+
_cache.RequestHandlerType = type;
180+
181+
await handler.Handle(provider, request, ct);
182+
return;
183+
}
184+
185+
if (_defaultHandler is null)
186+
{
187+
throw new HandlerNotFoundException($"No handler found for request type {requestType.FullName}.");
188+
}
189+
190+
var method = _defaultHandler
191+
.GetType()
192+
.GetMethods()
193+
.First(m => m.Name == nameof(IDefaultRequestHandler.Handle) && m.GetGenericArguments().Length == 1)
194+
.MakeGenericMethod(arguments);
195+
196+
await (ValueTask) method.Invoke(_defaultHandler, new object[] {_serviceProvider, request, ct})!;
197+
}
198+
}

src/Mediator.DependencyInjection/Generic/MediatorBuilder.RequestHandler.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ public IMediatorBuilder AddRequestHandler(
2929
public IMediatorBuilder AddRequestHandler(Type handlerType, RegistrationScope scope = RegistrationScope.Transient)
3030
{
3131
var handlers = handlerType.GetInterfaces()
32-
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IRequestHandler<,>))
32+
.Where(t => t.IsGenericType && (
33+
t.GetGenericTypeDefinition() == typeof(IRequestHandler<,>) ||
34+
t.GetGenericTypeDefinition() == typeof(IRequestHandler<>)
35+
))
3336
.ToArray();
3437

3538
foreach (var type in handlers)
3639
{
40+
var args = type.GetGenericArguments();
3741
var requestType = type.GetGenericArguments()[0];
38-
var responseType = type.GetGenericArguments()[1];
42+
var responseType = args.Length > 1 ? args[1] : null;
3943

4044
if (requestType.IsGenericType)
4145
{

src/Mediator.DependencyInjection/Handlers/Request/FuncRequestHandler.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,19 @@ public ValueTask<TResponse> Handle(IServiceProvider provider, TRequest request,
2121
return _invoke(provider, request, cancellationToken);
2222
}
2323
}
24+
25+
internal class FuncRequestHandler<TRequest> : IRequestHandler<TRequest>
26+
where TRequest : IRequest
27+
{
28+
private readonly Func<IServiceProvider, TRequest, CancellationToken, ValueTask> _invoke;
29+
30+
public FuncRequestHandler(Func<IServiceProvider, TRequest, CancellationToken, ValueTask> invoke)
31+
{
32+
_invoke = invoke;
33+
}
34+
35+
public ValueTask Handle(IServiceProvider provider, TRequest request, CancellationToken cancellationToken)
36+
{
37+
return _invoke(provider, request, cancellationToken);
38+
}
39+
}

src/Mediator.DependencyInjection/Handlers/Request/NamespaceRequestHandler.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,22 @@ public IRequestHandler<TRequest, TResponse> GetHandler(IServiceProvider provider
2121
return _handler;
2222
}
2323
}
24+
25+
internal class NamespaceRequestHandler<TRequest> : INamespaceRequestHandler<TRequest>
26+
where TRequest : IRequest
27+
{
28+
private readonly IRequestHandler<TRequest> _handler;
29+
30+
public NamespaceRequestHandler(MediatorNamespace ns, IRequestHandler<TRequest> handler)
31+
{
32+
Namespace = ns;
33+
_handler = handler;
34+
}
35+
36+
public MediatorNamespace Namespace { get; }
37+
38+
public IRequestHandler<TRequest> GetHandler(IServiceProvider provider)
39+
{
40+
return _handler;
41+
}
42+
}

src/Mediator.DependencyInjection/Handlers/Request/NamespaceRequestHandlerProvider.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,18 @@ public NamespaceRequestHandlerProvider(MediatorNamespace ns)
1818
public IRequestHandler<TRequest, TResponse> GetHandler(IServiceProvider provider)
1919
=> provider.GetRequiredService<THandler>();
2020
}
21+
22+
internal class NamespaceRequestHandlerProvider<TRequest, THandler> : INamespaceRequestHandler<TRequest>
23+
where TRequest : IRequest
24+
where THandler : class, IRequestHandler<TRequest>
25+
{
26+
public NamespaceRequestHandlerProvider(MediatorNamespace ns)
27+
{
28+
Namespace = ns;
29+
}
30+
31+
public MediatorNamespace Namespace { get; }
32+
33+
public IRequestHandler<TRequest> GetHandler(IServiceProvider provider)
34+
=> provider.GetRequiredService<THandler>();
35+
}

0 commit comments

Comments
 (0)