通过 DynamicProxy 拦截 returns generic Task<> 的异步方法

Intercept async method that returns generic Task<> via DynamicProxy

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

我想实现与 returns TaskTask<T> 结果的异步方法一起工作的拦截器。

我将下一个代码用于 return ContinueWith 结果(以便调用方方法在拦截器完成工作时等待)

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

上面的代码对于 Task 结果工作正常,但是如果 Task<T> 结果 ContinueWith 会将 return 类型从 Task<T> 更改为 Task. 我需要调用 returns Task<T> 的重载方法 ContinueWith,但为此我需要将 invocation.ReturnValue 转换为 Task<T>

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

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

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

这是我使用 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
        }
    }

更好的解决方案是使用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;
}

由于需要拦截返回 Task<TResult> 的方法,我创建了 Castle.Core 的扩展以简化流程。

Castle.Core.AsyncInterceptor

包可在 NuGet 下载。

解决方案主要基于 @silas-reinagel, but simplifies it by providing a new interface to implement IAsyncInterceptor 的答案。还有进一步的抽象使拦截类似于实现 Interceptor.

有关详细信息,请参阅项目的 readme

我这样做过:

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);
}

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

在我的例子中,我拦截了一个返回 Task 的异步方法,只有在 invocation.Proceed() 期间没有异常时它才应该执行 "after invocation.Proceed() code"。最后我使用了@James Skimming 的 code sample,所以这个解决方案只适用于拦截返回 Task 而不是 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()
    {
    }
}