-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGenericStreamRequestHandler.cs
More file actions
136 lines (109 loc) · 4.36 KB
/
Copy pathGenericStreamRequestHandler.cs
File metadata and controls
136 lines (109 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Collections.Generic;
using System.Threading;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
namespace Zapto.Mediator;
internal sealed class GenericStreamRequestRegistration
{
public GenericStreamRequestRegistration(Type requestType, Type? responseType, Type handlerType)
{
RequestType = requestType;
ResponseType = responseType;
HandlerType = handlerType;
}
public Type RequestType { get; }
public Type? ResponseType { get; }
public Type HandlerType { get; }
}
internal sealed class GenericStreamRequestCache<TRequest, TResponse>
{
public GenericStreamRequestCache(IEnumerable<GenericStreamRequestRegistration> registrations)
{
var requestType = typeof(TRequest);
if (requestType.IsGenericType)
{
var genericType = requestType.GetGenericTypeDefinition();
MatchingRegistrations = GenericTypeHelper.CacheMatchingRegistrations(
registrations,
r => r.RequestType,
genericType);
}
else
{
MatchingRegistrations = new List<GenericStreamRequestRegistration>();
}
}
private Type? _requestHandlerType;
public Type? RequestHandlerType { get => Volatile.Read(ref _requestHandlerType); set => Volatile.Write(ref _requestHandlerType, value); }
public List<GenericStreamRequestRegistration> MatchingRegistrations { get; }
}
internal sealed class GenericStreamRequestHandler<TRequest, TResponse> : IStreamRequestHandler<TRequest, TResponse>
where TRequest : IStreamRequest<TResponse>
{
private readonly GenericStreamRequestCache<TRequest, TResponse> _cache;
private readonly IServiceProvider _serviceProvider;
private readonly IDefaultStreamRequestHandler? _defaultHandler;
public GenericStreamRequestHandler(
IServiceProvider serviceProvider,
GenericStreamRequestCache<TRequest, TResponse> cache,
IDefaultStreamRequestHandler? defaultHandler = null)
{
_serviceProvider = serviceProvider;
_cache = cache;
_defaultHandler = defaultHandler;
}
public IAsyncEnumerable<TResponse> Handle(IServiceProvider provider, TRequest request, CancellationToken ct)
{
if (_cache.RequestHandlerType is {} cachedType)
{
var handler = _serviceProvider.GetRequiredService(cachedType);
return ((IStreamRequestHandler<TRequest, TResponse>)handler).Handle(provider, request, ct);
}
var requestType = typeof(TRequest);
var responseType = typeof(TResponse);
if (!requestType.IsGenericType)
{
if (_defaultHandler is null)
{
throw new HandlerNotFoundException($"No handler found for request type {requestType.FullName}.");
}
return _defaultHandler.Handle<TRequest, TResponse>(provider, request, ct);
}
var arguments = requestType.GetGenericArguments();
requestType = requestType.GetGenericTypeDefinition();
if (responseType.IsGenericType)
{
responseType = responseType.GetGenericTypeDefinition();
}
foreach (var registration in _cache.MatchingRegistrations)
{
if (registration.ResponseType is not null && registration.ResponseType != responseType)
{
continue;
}
Type type;
if (registration.HandlerType.IsGenericType)
{
// Check if the generic arguments satisfy the handler's constraints
if (!GenericTypeHelper.CanMakeGenericType(registration.HandlerType, arguments))
{
continue;
}
type = registration.HandlerType.MakeGenericType(arguments);
}
else
{
type = registration.HandlerType;
}
var handler = (IStreamRequestHandler<TRequest, TResponse>) _serviceProvider.GetRequiredService(type);
_cache.RequestHandlerType = type;
return handler.Handle(provider, request, ct);
}
if (_defaultHandler is null)
{
throw new HandlerNotFoundException($"No handler found for request type {requestType.FullName}.");
}
return _defaultHandler.Handle<TRequest, TResponse>(provider, request, ct);
}
}