Castle 动态代理-异步方法的代理

Castle DynamicProxy 动态代理-异步方法的代理(C#)

  • Castle Core版本 v4.4.0 Github
  • .net core 2.2

上一篇文章中我们介绍了Castle动态代理对于同步方法的动态代理,那么这篇文章就直接进入主题介绍如何使用Castle来对异步方法进行代理。

为何对异步方法会失效

首先为什么Castle会对异步方法(async-await)的动态代理失效?

还是考虑如下跟同步方法一样的代码:

public class SomeInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //Do before...
        invocation.Proceed();
        //Do after...
    }
}

当对异步方法进行代理的时候当调用到拦截器链底部,并且调用invocation.Proceed()执行真正的被代理方法的时候由于被代理方法是异步的,所以当在异步方法里面碰到await语句的时候,被代理方法会马上返回,然后执行invocation.Proceed()后面的Do after。然而此时真正的被代理方法其实还没有执行完成。

所以就造成了Do after在被代理方法执行完成之前就已经执行了。

下面就介绍如何对这种异步方法进行动态代理。

返回值为Task的方法

对于返回值为Task的异步方法可以进行如下处理:

public class SomeInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //Do before...
        invocation.Proceed();

        var returnType = invocation.Method.ReturnType; //获取被代理方法的返回类型
        if(returnType != null && returnType == typeof(Task)) //判断如果为Task类型
        {
            Func<Task> continuation = async () =>         //定义一个异步方法来等待方法返回的Task
            {
                await (Task)invocation.ReturnValue;

                //Do after...                             //方法返回后调用的代码
            };

            invocation.ReturnValue = continuation();      //设置返回值为刚刚定义的异步方法
            return;
        }        
    }
}

由于被代理方法是异步的,所以当调用invocation.Proceed()时会马上返回,继续执行Intercept下面的代码,而下面的代码是根据被代理方法的类型来判断是否是异步方法,然后通过构造一个匿名的函数来等待被代理方法返回,并且在下面继续编写返回后执行的代码。最后再通过设置invocation.ReturnValue = continuation()来使方法阻塞在continuation()实现了等待的效果。

注意invocation.ReturnValue = continuation()中并没有使用await,所以continuation()就如同步方法一样是阻塞的。

返回值为Task的方法

为什么对于异步方法的代理需要按照返回值分开讨论?

最主要的原因在于Task的返回值是泛型类型,而Task是固定的一种类型。

对与Task的异步方法代理的处理跟Task类型的返回值的思路是一样的,唯一有一点不同的是需要利用反射来获取方法中的泛型类型,进而用来构造一个跟被代理方法返回值一样的临时方法

代码如下:

public class SomeInterceptor : IInterceptor
{
    //利用反射获得等待返回值的异步方法,以便在后面构造泛型参数
    private MethodInfo methodInfo = typeof(SomeInterceptor).GetMethod("HandleAsync", BindingFlags.Instance | BindingFlags.Public);

    public void Intercept(IInvocation invocation)
    {
        //Do before...
        invocation.Proceed();

        var returnType = invocation.Method.ReflectedType;     //获取被代理方法的返回类型
        if(returnType != null && returnType.GetGenericTypeDefinition() == typeof(Task<>))
        {                        
            HandleAsyncWithReflection(invocation);
        }
    }

    //利用反射获取方法类型信息来构造等待返回值的异步方法
    private void HandleAsyncWithReflection(IInvocation invocation)
    {
        var resultType = invocation.Method.ReturnType.GetGenericArguments()[0];
        var mi = methodInfo.MakeGenericMethod(resultType);
        invocation.ReturnValue = mi.Invoke(this,new[] {invocation.ReturnValue});
    }

    //构造等待返回值的异步方法
    public async Task<T> HandleAsync<T> (Task<T> task)
    {
        var t = await task;

        //Do arter

        return t;
    }
}

上面的代码可能有少许的复杂,不过不要紧下面我就来解释一下为什么要这么做。

首先在类的开头多了一个methodInfo字段,该字段是通过反射获得该类型中的方法名为HandleAsync的方法信息。

然后我们再看正常的Intercept()方法,这跟之前讨论的Task返回值的一样,还是调用invocation.Proceed(),然后还是判断返回值类型,不过这里是returnType.GetGenericTypeDefinition() == typeof(Task<>)来判断是否为泛型的Task<>对象。

然后如果是Task<>类型的话就调用HandleAsyncWithReflection()方法,那么这个方法是什么作用呢,看该方法的实现可以看到,该方法首先获取了一个泛型参数,即Task中实际的T类型,然后调用methodInfo字段的MakeGenericMethod()方法,用获得的类型T来重新构造HandleAsync()方法。

然后还是跟之前一样,给invocation.ReturnValue赋值,只是这时候是通过反射的方式来调用HandleAsync()方法,进而等待方法执行完成并继续后面的动作。

总结

以上就是Castle动态代理中实现对异步方法的代理的解决方案,那么综合前面一篇文章Castle可以对所有的方法都提供动态代理,并且在github上也有关于用Castle来进行异步动态代理的库能减少一些代码量。

Castle.Core.AsyncInterceptor库的github地址 Castle.Core.AsyncInterceptor

本人的公众号,有空就写写这样子,谢谢关注

你可能感兴趣的:(编程综合)