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

Версия 1.1 от Alexandr Fokin на 2025/05/11 11:25

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