Исходный код вики Универсальный перехватчик
Версия 1.1 от Alexandr Fokin на 2025/05/11 11:25
Последние авторы
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. Таким образом можно реализовать АОП подход без использования [[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 |