Исходный код вики Универсальный перехватчик
Редактировал(а) Alexandr Fokin 2025/09/07 14:52
Последние авторы
author | version | line-number | content |
---|---|---|---|
1 | | |Универсальный перехватчик, Универсальный декоратор. | ||
2 | | |((( | ||
3 | 1. Используя функционал [[DLR>>doc:Разработка.NET.C#.Рантайм и типы.DLR.WebHome]] можно реализовать перехватчик, оборачивающий любой метод. | ||
4 | 11. Библиотека ImpromptuInterface предоставляет функционал создания обертки над DynamicObject, которую является реализацией интерфейса (не наследую его явно). | ||
5 | 1. Используя функционал для декорирования (например Scrutor) при регистрации сервисов в DI можно автоматически оборачивать компоненты (сервисы) в универсальный декоратор. | ||
6 | 11. Таким образом можно реализовать [[AOP>>doc:Разработка.NET.Библиотеки.DI IOC AOP.WebHome]] подход без использования [[MediatR>>doc:Разработка.NET.Библиотеки.DI IOC AOP.MediatR.WebHome]]. | ||
7 | |||
8 | Также еще можно сделать реализацию на основе [[Castle DynamicProxy>>doc:Разработка.NET.Библиотеки.DI IOC AOP.AOP.Castle DynamicProxy.WebHome]] | ||
9 | ))) | ||
10 | | |((( | ||
11 | Набросок | ||
12 | |||
13 | |(% style="width:944px" %){{code language="c#"}}public class UniversalIntersepter | ||
14 | : DynamicObject | ||
15 | { | ||
16 | private readonly Func<object> _subject; | ||
17 | private readonly IIntersepterMiddleware _intersepterMiddleware; | ||
18 | |||
19 | public UniversalIntersepter( | ||
20 | object subject, | ||
21 | IIntersepterMiddleware intersepterMiddleware) | ||
22 | { | ||
23 | _subject = () => subject; | ||
24 | _intersepterMiddleware = intersepterMiddleware; | ||
25 | } | ||
26 | |||
27 | public UniversalIntersepter(Func<object> subjectFactory) | ||
28 | { | ||
29 | _subject = subjectFactory; | ||
30 | } | ||
31 | |||
32 | public override bool TryInvokeMember( | ||
33 | InvokeMemberBinder binder, | ||
34 | object[] args, | ||
35 | out object result) | ||
36 | { | ||
37 | var instance = _subject(); | ||
38 | var method = instance | ||
39 | .GetType() | ||
40 | // TODO: не учитывает перегрузки. | ||
41 | .GetMethod(binder.Name)!; | ||
42 | |||
43 | if (method == null) | ||
44 | { | ||
45 | result = default!; | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | if (method.ReturnType == typeof(Task)) | ||
50 | { | ||
51 | result = new TaskHandler(this, instance, method, binder, args) | ||
52 | .HandleAsync(); | ||
53 | } | ||
54 | else if (method.ReturnType.BaseType == typeof(Task)) | ||
55 | { | ||
56 | var genericWrapperType = typeof(TaskHandler<>) | ||
57 | .MakeGenericType(method.ReturnType.GenericTypeArguments[0]); | ||
58 | var wrapper = Activator.CreateInstance(genericWrapperType, this, instance, method, binder, args); | ||
59 | |||
60 | result = wrapper.GetType() | ||
61 | .GetMethod(nameof(TaskHandler.HandleAsync)) | ||
62 | .Invoke(wrapper, null); | ||
63 | } | ||
64 | else if (method.ReturnType == typeof(ValueTask)) | ||
65 | { | ||
66 | result = new ValueTaskTaskHandler(this, instance, method, binder, args) | ||
67 | .HandleAsync(); | ||
68 | } | ||
69 | else if (method.ReturnType.BaseType == typeof(ValueTask)) | ||
70 | { | ||
71 | var genericWrapperType = typeof(ValueTaskHandler<>) | ||
72 | .MakeGenericType(method.ReturnType.GenericTypeArguments[0]); | ||
73 | var wrapper = Activator.CreateInstance(genericWrapperType, this, instance, method, binder, args); | ||
74 | |||
75 | result = wrapper.GetType() | ||
76 | .GetMethod(nameof(TaskHandler.HandleAsync)) | ||
77 | .Invoke(wrapper, null); | ||
78 | } | ||
79 | else | ||
80 | { | ||
81 | _intersepterMiddleware.BeforeExecute(binder, args); | ||
82 | result = method.Invoke(instance, args); | ||
83 | _intersepterMiddleware.AfterExecute(binder, args, result); | ||
84 | } | ||
85 | |||
86 | return true; | ||
87 | } | ||
88 | |||
89 | |||
90 | public object ToType(Type interfaceType) | ||
91 | { | ||
92 | var result = Impromptu.DynamicActLike(this, interfaceType); | ||
93 | return result; | ||
94 | } | ||
95 | |||
96 | private class TaskHandler | ||
97 | { | ||
98 | private readonly UniversalIntersepter _universalIntersepter; | ||
99 | private readonly object _subject; | ||
100 | private readonly MethodInfo _methodInfo; | ||
101 | private readonly InvokeMemberBinder _binder; | ||
102 | private readonly object[] _args; | ||
103 | |||
104 | public TaskHandler( | ||
105 | UniversalIntersepter universalIntersepter, | ||
106 | object subject, | ||
107 | MethodInfo methodInfo, | ||
108 | InvokeMemberBinder binder, | ||
109 | object[] args) | ||
110 | { | ||
111 | _universalIntersepter = universalIntersepter; | ||
112 | _subject = subject; | ||
113 | _methodInfo = methodInfo; | ||
114 | _binder = binder; | ||
115 | _args = args; | ||
116 | } | ||
117 | |||
118 | public async Task HandleAsync() | ||
119 | { | ||
120 | await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); | ||
121 | await (Task)_methodInfo.Invoke(_subject, _args); | ||
122 | await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, null); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | private class ValueTaskTaskHandler | ||
127 | { | ||
128 | private readonly UniversalIntersepter _universalIntersepter; | ||
129 | private readonly object _subject; | ||
130 | private readonly MethodInfo _methodInfo; | ||
131 | private readonly InvokeMemberBinder _binder; | ||
132 | private readonly object[] _args; | ||
133 | |||
134 | public ValueTaskTaskHandler( | ||
135 | UniversalIntersepter universalIntersepter, | ||
136 | object subject, | ||
137 | MethodInfo methodInfo, | ||
138 | InvokeMemberBinder binder, | ||
139 | object[] args) | ||
140 | { | ||
141 | _universalIntersepter = universalIntersepter; | ||
142 | _subject = subject; | ||
143 | _methodInfo = methodInfo; | ||
144 | _binder = binder; | ||
145 | _args = args; | ||
146 | } | ||
147 | |||
148 | public async ValueTask HandleAsync() | ||
149 | { | ||
150 | await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); | ||
151 | await (ValueTask)_methodInfo.Invoke(_subject, _args); | ||
152 | await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, null); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | private class TaskHandler<T> | ||
157 | { | ||
158 | private readonly UniversalIntersepter _universalIntersepter; | ||
159 | private readonly object _subject; | ||
160 | private readonly MethodInfo _methodInfo; | ||
161 | private readonly InvokeMemberBinder _binder; | ||
162 | private readonly object[] _args; | ||
163 | |||
164 | public TaskHandler( | ||
165 | UniversalIntersepter universalIntersepter, | ||
166 | object subject, | ||
167 | MethodInfo methodInfo, | ||
168 | InvokeMemberBinder binder, | ||
169 | object[] args) | ||
170 | { | ||
171 | _universalIntersepter = universalIntersepter; | ||
172 | _subject = subject; | ||
173 | _methodInfo = methodInfo; | ||
174 | _binder = binder; | ||
175 | _args = args; | ||
176 | } | ||
177 | |||
178 | public async Task<T> HandleAsync() | ||
179 | { | ||
180 | await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); | ||
181 | var result = await (Task<T>)_methodInfo.Invoke(_subject, _args); | ||
182 | await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, result); | ||
183 | |||
184 | return result; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | private class ValueTaskHandler<T> | ||
189 | { | ||
190 | private readonly UniversalIntersepter _universalIntersepter; | ||
191 | private readonly object _subject; | ||
192 | private readonly MethodInfo _methodInfo; | ||
193 | private readonly InvokeMemberBinder _binder; | ||
194 | private readonly object[] _args; | ||
195 | |||
196 | public ValueTaskHandler( | ||
197 | UniversalIntersepter universalIntersepter, | ||
198 | object subject, | ||
199 | MethodInfo methodInfo, | ||
200 | InvokeMemberBinder binder, | ||
201 | object[] args) | ||
202 | { | ||
203 | _universalIntersepter = universalIntersepter; | ||
204 | _subject = subject; | ||
205 | _methodInfo = methodInfo; | ||
206 | _binder = binder; | ||
207 | _args = args; | ||
208 | } | ||
209 | |||
210 | public async ValueTask<T> HandleAsync() | ||
211 | { | ||
212 | await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); | ||
213 | var result = await (ValueTask<T>)_methodInfo.Invoke(_subject, _args); | ||
214 | await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, result); | ||
215 | |||
216 | return result; | ||
217 | } | ||
218 | } | ||
219 | }{{/code}}|(% style="width:452px" %)((( | ||
220 | 1. Это набросок, показывающий сам смысл. | ||
221 | 11. Можно оптимизировать работу с рефлексией. | ||
222 | 1. _intersepterMiddleware представить как IReadonlyList<IIntersepterMiddleware > | ||
223 | 1. При реализации требуется костыль для поддержки асинхронных методов. | ||
224 | 11. Если оборачиваемый метод синхронный, то возможен только синхронный декоратор. | ||
225 | ))) | ||
226 | |(% style="width:944px" %){{code language="c#"}}public static class ServiceCollectionExtension | ||
227 | { | ||
228 | public static IServiceCollection WrapUniversalDecorator( | ||
229 | this IServiceCollection services, | ||
230 | Func<ServiceDescriptor, bool>? needWrapAction = null) | ||
231 | { | ||
232 | needWrapAction = needWrapAction ?? ((_) => true); | ||
233 | |||
234 | foreach (var elem in services.Select(e => e).ToArray()) | ||
235 | { | ||
236 | // Работает только для интерфейсов. | ||
237 | if (!elem.ServiceType.IsInterface) | ||
238 | { | ||
239 | continue; | ||
240 | } | ||
241 | |||
242 | // Не оборачиваем сам middleware | ||
243 | if (elem.ServiceType == typeof(IIntersepterMiddleware)) | ||
244 | { | ||
245 | continue; | ||
246 | } | ||
247 | |||
248 | if (!needWrapAction(elem)) | ||
249 | { | ||
250 | continue; | ||
251 | } | ||
252 | |||
253 | // Оборачиваем в универсальный декоратор. | ||
254 | services.Decorate( | ||
255 | serviceType: elem.ServiceType, | ||
256 | decorator: | ||
257 | (e, s) => new UniversalIntersepter( | ||
258 | e, | ||
259 | s.GetRequiredService<IIntersepterMiddleware>() | ||
260 | ) | ||
261 | .ToType(elem.ServiceType) | ||
262 | ); | ||
263 | } | ||
264 | |||
265 | return services; | ||
266 | }{{/code}}|(% style="width:452px" %)((( | ||
267 | 1. Декорируются только интерфейсы, не строгие типы. | ||
268 | 1. В декоратор оборачивается интерфейс типа, поэтому если реализация внутри напрямую вызовет свой метод, то он не будет обернут. | ||
269 | ))) | ||
270 | |(% style="width:944px" %){{code language="c#"}}ValueTask BeforeExecuteAsync( | ||
271 | InvokeMemberBinder binder, | ||
272 | object[] args); | ||
273 | |||
274 | void BeforeExecute( | ||
275 | InvokeMemberBinder binder, | ||
276 | object[] args); | ||
277 | |||
278 | ValueTask AfterExecuteAsync( | ||
279 | InvokeMemberBinder binder, | ||
280 | object[] args, | ||
281 | object? result); | ||
282 | |||
283 | void AfterExecute( | ||
284 | InvokeMemberBinder binder, | ||
285 | object[] args, | ||
286 | object? result);{{/code}}|(% style="width:452px" %)Можно переделать на формат middleware, когда идет один метод Execute, принимающий nextHandler. | ||
287 | Пока что для простоты не запаривался. | ||
288 | |||
289 | |||
290 | ))) |