| Набросок public class UniversalIntersepter : DynamicObject { private readonly Func<object> _subject; private readonly IIntersepterMiddleware _intersepterMiddleware;
public UniversalIntersepter( object subject, IIntersepterMiddleware intersepterMiddleware) { _subject = () => subject; _intersepterMiddleware = intersepterMiddleware; }
public UniversalIntersepter(Func<object> subjectFactory) { _subject = subjectFactory; }
public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { var instance = _subject(); var method = instance .GetType() // TODO: не учитывает перегрузки. .GetMethod(binder.Name)!;
if (method == null) { result = default!; return false; }
if (method.ReturnType == typeof(Task)) { result = new TaskHandler(this, instance, method, binder, args) .HandleAsync(); } else if (method.ReturnType.BaseType == typeof(Task)) { var genericWrapperType = typeof(TaskHandler<>) .MakeGenericType(method.ReturnType.GenericTypeArguments[0]); var wrapper = Activator.CreateInstance(genericWrapperType, this, instance, method, binder, args);
result = wrapper.GetType() .GetMethod(nameof(TaskHandler.HandleAsync)) .Invoke(wrapper, null); } else if (method.ReturnType == typeof(ValueTask)) { result = new ValueTaskTaskHandler(this, instance, method, binder, args) .HandleAsync(); } else if (method.ReturnType.BaseType == typeof(ValueTask)) { var genericWrapperType = typeof(ValueTaskHandler<>) .MakeGenericType(method.ReturnType.GenericTypeArguments[0]); var wrapper = Activator.CreateInstance(genericWrapperType, this, instance, method, binder, args);
result = wrapper.GetType() .GetMethod(nameof(TaskHandler.HandleAsync)) .Invoke(wrapper, null); } else { _intersepterMiddleware.BeforeExecute(binder, args); result = method.Invoke(instance, args); _intersepterMiddleware.AfterExecute(binder, args, result); }
return true; }
public object ToType(Type interfaceType) { var result = Impromptu.DynamicActLike(this, interfaceType); return result; }
private class TaskHandler { private readonly UniversalIntersepter _universalIntersepter; private readonly object _subject; private readonly MethodInfo _methodInfo; private readonly InvokeMemberBinder _binder; private readonly object[] _args;
public TaskHandler( UniversalIntersepter universalIntersepter, object subject, MethodInfo methodInfo, InvokeMemberBinder binder, object[] args) { _universalIntersepter = universalIntersepter; _subject = subject; _methodInfo = methodInfo; _binder = binder; _args = args; }
public async Task HandleAsync() { await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); await (Task)_methodInfo.Invoke(_subject, _args); await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, null); } }
private class ValueTaskTaskHandler { private readonly UniversalIntersepter _universalIntersepter; private readonly object _subject; private readonly MethodInfo _methodInfo; private readonly InvokeMemberBinder _binder; private readonly object[] _args;
public ValueTaskTaskHandler( UniversalIntersepter universalIntersepter, object subject, MethodInfo methodInfo, InvokeMemberBinder binder, object[] args) { _universalIntersepter = universalIntersepter; _subject = subject; _methodInfo = methodInfo; _binder = binder; _args = args; }
public async ValueTask HandleAsync() { await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); await (ValueTask)_methodInfo.Invoke(_subject, _args); await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, null); } }
private class TaskHandler<T> { private readonly UniversalIntersepter _universalIntersepter; private readonly object _subject; private readonly MethodInfo _methodInfo; private readonly InvokeMemberBinder _binder; private readonly object[] _args;
public TaskHandler( UniversalIntersepter universalIntersepter, object subject, MethodInfo methodInfo, InvokeMemberBinder binder, object[] args) { _universalIntersepter = universalIntersepter; _subject = subject; _methodInfo = methodInfo; _binder = binder; _args = args; }
public async Task<T> HandleAsync() { await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); var result = await (Task<T>)_methodInfo.Invoke(_subject, _args); await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, result);
return result; } }
private class ValueTaskHandler<T> { private readonly UniversalIntersepter _universalIntersepter; private readonly object _subject; private readonly MethodInfo _methodInfo; private readonly InvokeMemberBinder _binder; private readonly object[] _args;
public ValueTaskHandler( UniversalIntersepter universalIntersepter, object subject, MethodInfo methodInfo, InvokeMemberBinder binder, object[] args) { _universalIntersepter = universalIntersepter; _subject = subject; _methodInfo = methodInfo; _binder = binder; _args = args; }
public async ValueTask<T> HandleAsync() { await _universalIntersepter._intersepterMiddleware.BeforeExecuteAsync(_binder, _args); var result = await (ValueTask<T>)_methodInfo.Invoke(_subject, _args); await _universalIntersepter._intersepterMiddleware.AfterExecuteAsync(_binder, _args, result);
return result; } } } | - Это набросок, показывающий сам смысл.
- Можно оптимизировать работу с рефлексией.
- _intersepterMiddleware представить как IReadonlyList<IIntersepterMiddleware >
- При реализации требуется костыль для поддержки асинхронных методов.
- Если оборачиваемый метод синхронный, то возможен только синхронный декоратор.
| public static class ServiceCollectionExtension { public static IServiceCollection WrapUniversalDecorator( this IServiceCollection services, Func<ServiceDescriptor, bool>? needWrapAction = null) { needWrapAction = needWrapAction ?? ((_) => true);
foreach (var elem in services.Select(e => e).ToArray()) { // Работает только для интерфейсов. if (!elem.ServiceType.IsInterface) { continue; }
// Не оборачиваем сам middleware if (elem.ServiceType == typeof(IIntersepterMiddleware)) { continue; }
if (!needWrapAction(elem)) { continue; }
// Оборачиваем в универсальный декоратор. services.Decorate( serviceType: elem.ServiceType, decorator: (e, s) => new UniversalIntersepter( e, s.GetRequiredService<IIntersepterMiddleware>() ) .ToType(elem.ServiceType) ); } return services; } | - Декорируются только интерфейсы, не строгие типы.
- В декоратор оборачивается интерфейс типа, поэтому если реализация внутри напрямую вызовет свой метод, то он не будет обернут.
| ValueTask BeforeExecuteAsync( InvokeMemberBinder binder, object[] args);
void BeforeExecute( InvokeMemberBinder binder, object[] args);
ValueTask AfterExecuteAsync( InvokeMemberBinder binder, object[] args, object? result);
void AfterExecute( InvokeMemberBinder binder, object[] args, object? result); | Можно переделать на формат middleware, когда идет один метод Execute, принимающий nextHandler. Пока что для простоты не запаривался. |
|