代码之家  ›  专栏  ›  技术社区  ›  Anatoliy

拦截通过DynamicProxy返回泛型Task的异步方法

  •  28
  • Anatoliy  · 技术社区  · 10 年前

    我的问题与此帖子有关 Intercept the call to an async method using DynamicProxy

    我希望实现与异步方法一起工作的拦截器 Task Task<T> 后果

    我使用下一个代码返回 ContinueWith result(在拦截器完成工作时调用方方法等待的顺序)

    var task = invocation.ReturnValue as Task;
    invocation.ReturnValue = task.ContinueWith(c => 
          { code that should execute after method finish });
    

    以上代码适用于 任务 结果,但如果 任务<T> 后果 继续 将从更改返回类型 任务<T> 任务 . 我需要调用重载方法ContinueWith返回 任务<T> 但为此我需要 invocation.ReturnValue 任务<T>

    我没有找到以任何方式动态投射它的方法。 有人知道怎么做吗?

    我也尝试通过反射调用这个方法,但参数是不能直接传递的labmda函数。

    5 回复  |  直到 7 年前
        1
  •  23
  •   Silas Reinagel    10 年前

    经过广泛的研究,我能够创建一个用于拦截同步方法以及异步任务和异步任务的解决方案<T结果>。

    这是我的异常处理拦截器代码,它使用Castle Dynamic Proxy处理所有这些方法类型。这种模式适用于进行任何类型的拦截。对于标准的BeforeInvoke/AfterInvoke操作,语法会更清晰一些,但概念应该相同。

    (其他注意:示例中的IExceptionHandler接口是一个自定义类型,而不是通用对象。)

        private class AsyncExceptionHandlingInterceptor : IInterceptor
        {
            private static readonly MethodInfo handleAsyncMethodInfo = typeof(AsyncExceptionHandlingInterceptor).GetMethod("HandleAsyncWithResult", BindingFlags.Instance | BindingFlags.NonPublic);
            private readonly IExceptionHandler _handler;
    
            public AsyncExceptionHandlingInterceptor(IExceptionHandler handler)
            {
                _handler = handler;
            }
    
            public void Intercept(IInvocation invocation)
            {
                var delegateType = GetDelegateType(invocation);
                if (delegateType == MethodType.Synchronous)
                {
                    _handler.HandleExceptions(() => invocation.Proceed());
                }
                if (delegateType == MethodType.AsyncAction)
                {
                    invocation.Proceed();
                    invocation.ReturnValue = HandleAsync((Task)invocation.ReturnValue);
                }
                if (delegateType == MethodType.AsyncFunction)
                {
                    invocation.Proceed();
                    ExecuteHandleAsyncWithResultUsingReflection(invocation);
                }
            }
    
            private void ExecuteHandleAsyncWithResultUsingReflection(IInvocation invocation)
            {
                var resultType = invocation.Method.ReturnType.GetGenericArguments()[0];
                var mi = handleAsyncMethodInfo.MakeGenericMethod(resultType);
                invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue });
            }
    
            private async Task HandleAsync(Task task)
            {
                await _handler.HandleExceptions(async () => await task);
            }
    
            private async Task<T> HandleAsyncWithResult<T>(Task<T> task)
            {
                return await _handler.HandleExceptions(async () => await task);
            }
    
            private MethodType GetDelegateType(IInvocation invocation)
            {
                var returnType = invocation.Method.ReturnType;
                if (returnType == typeof(Task))
                    return MethodType.AsyncAction;
                if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
                    return MethodType.AsyncFunction;
                return MethodType.Synchronous;
            }
    
            private enum MethodType
            {
                Synchronous,
                AsyncAction,
                AsyncFunction
            }
        }
    
        2
  •  16
  •   thepirat000    8 年前

    更好的解决方案是使用 dynamic 关键字以绕过编译器类型检查并在运行时解析操作:

    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
        var method = invocation.MethodInvocationTarget;
        var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
        if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
        {
            invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue);
        }
    }
    
    private static async Task InterceptAsync(Task task)
    {
        await task.ConfigureAwait(false);
        // do the continuation work for Task...
    }
    
    private static async Task<T> InterceptAsync<T>(Task<T> task)
    {
        T result = await task.ConfigureAwait(false);
        // do the continuation work for Task<T>...
        return result;
    }
    
        3
  •  7
  •   Community CDub    7 年前

    需要拦截返回的方法 Task<TResult> ,我创建了一个扩展 Castle.Core 这简化了过程。

    Castle.Core.AsyncInterceptor

    该软件包可在上下载 NuGet .

    解决方案主要基于 @silas-reinagel ,但通过提供新的接口来实现 IAsyncInterceptor 。还有进一步的抽象,使拦截类似于实现 Interceptor .

    请参见 readme 了解更多详情。

        4
  •  0
  •   xhafan    5 年前

    @Silas Reinagel和@thepirat000的解决方案对我不起作用,我也没有成功地使用 Castle.Core.AsyncInterceptor 来自@James Skimming的解决方案。

    在我的例子中,我拦截了一个返回的异步方法 Task ,并且只有在调用过程中没有异常时,它才应该执行“after invocation.Proceed()code” invocation.Proceed() 。最后我用了@James Skimming的 code sample ,因此此解决方案仅适用于拦截返回的异步方法 任务 而不是 Task<TResult> :

    public void Intercept(IInvocation invocation)
    {
        _stepPriorInvocation();
    
        invocation.Proceed();
        Func<Task> continuation = async () =>
        {
            await (Task)invocation.ReturnValue;
    
            _stepAfterSuccessfulInvocation();
        };
    
        invocation.ReturnValue = continuation();
    
        void _stepPriorInvocation()
        {
        }
    
        void _stepAfterSuccessfulInvocation()
        {
        }
    }
    
    
        5
  •  -1
  •   Luiz Coelho    6 年前

    我是这样做的:

    invocation.Proceed(); 
    object response;
    Type type = invocation.ReturnValue?.GetType();
    if (type != null && typeof(Task).IsAssignableFrom(type))
    {
        var resultProperty = type.GetProperty("Result");
        response = resultProperty.GetValue(invocation.ReturnValue);
    }