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.ReflectedType; //獲取被代理方法的返回類型
        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<T>的方法

爲什麼對於異步方法的代理需要按照返回值分開討論?

最主要的原因在於Task<T>的返回值是泛型類型,而Task是固定的一種類型。

對與Task<T>的異步方法代理的處理跟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,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>中實際的T類型,然後調用methodInfo字段的MakeGenericMethod()方法,用獲得的類型T來重新構造HandleAsync()方法。

然後還是跟之前一樣,給invocation.ReturnValue賦值,只是這時候是通過反射的方式來調用HandleAsync()方法,進而等待方法執行完成並繼續後面的動作。

總結

以上就是Castle動態代理中實現對異步方法的代理的解決方案,那麼綜合前面一篇文章Castle可以對所有的方法都提供動態代理,並且在github上也有關於用Castle來進行異步動態代理的庫能減少一些代碼量。

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

本人的公衆號,有空就寫寫這樣子,謝謝關注
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章