Исходный код вики Универсальный перехватчик

Редактировал(а) Alexandr Fokin 2025/09/07 14:52

Последние авторы
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 )))