2016-09-19 37 views
2

我們基本上有一個如下所示的類,它使用Castle.DynamicProxy進行攔截。是否可以使用Castle.DynamicProxy創建異步inteceptor?

using System; 
using System.Collections.Concurrent; 
using System.Reflection; 
using System.Threading; 
using System.Threading.Tasks; 
using Castle.DynamicProxy; 

namespace SaaS.Core.IoC 
{ 
    public abstract class AsyncInterceptor : IInterceptor 
    { 
     private readonly ILog _logger; 

     private readonly ConcurrentDictionary<Type, Func<Task, IInvocation, Task>> wrapperCreators = 
      new ConcurrentDictionary<Type, Func<Task, IInvocation, Task>>(); 

     protected AsyncInterceptor(ILog logger) 
     { 
      _logger = logger; 
     } 

     void IInterceptor.Intercept(IInvocation invocation) 
     { 
      if (!typeof(Task).IsAssignableFrom(invocation.Method.ReturnType)) 
      { 
       InterceptSync(invocation); 
       return; 
      } 

      try 
      { 
       CheckCurrentSyncronizationContext(); 
       var method = invocation.Method; 

       if ((method != null) && typeof(Task).IsAssignableFrom(method.ReturnType)) 
       { 
        var taskWrapper = GetWrapperCreator(method.ReturnType); 
        Task.Factory.StartNew(
         async() => { await InterceptAsync(invocation, taskWrapper).ConfigureAwait(true); } 
         , // this will use current synchronization context 
         CancellationToken.None, 
         TaskCreationOptions.AttachedToParent, 
         TaskScheduler.FromCurrentSynchronizationContext()).Wait(); 
       } 
      } 
      catch (Exception ex) 
      { 
       //this is not really burring the exception 
       //excepiton is going back in the invocation.ReturnValue which 
       //is a Task that failed. with the same excpetion 
       //as ex. 
      } 
     } 
.... 

最初,這個代碼是:

Task.Run(async() => { await InterceptAsync(invocation, taskWrapper)).Wait() 

但是,我們這個任意調用後失去的HttpContext,所以我們不得不將其切換到:

Task.Factory.StartNew 

因此,我們可以通過在TaskScheduler.FromCurrentSynchronizationContext()

所有這些都不好,因爲我們真的只是s爲另一個線程轉移一個線程。我真的很喜歡的

void IInterceptor.Intercept(IInvocation invocation) 

簽名更改爲

async Task IInterceptor.Intercept(IInvocation invocation) 

,擺脫了Task.Run或Task.Factory的,只是使它:

await InterceptAsync(invocation, taskWrapper); 

問題是Castle.DynamicProxy IInterecptor不會允許這個。我真的很想在攔截中等待。我可以做.Result,但那麼我打電話的異步呼叫有什麼意義?如果不能夠做到這一點,我就會失去它能夠產生這個線程執行的好處。我沒有卡住Castle Windsor的DynamicProxy,所以我正在尋找另一種方法來做到這一點。我們研究過Unity,但我不想替換我們的整個AutoFac實現。

任何幫助,將不勝感激。

回答

3

所有這一切都不好,因爲我們只是換一個線程換另一個線程。

是的。還因爲StartNew版本實際上並未等待該方法完成;它只會等到第一個await。但是如果你添加一個Unwrap()來使它等待完整的方法,那麼我強烈懷疑你最終會陷入僵局。

問題是Castle.DynamicProxy IInterecptor不會允許這個。

IInterceptor確實有一個設計上的限制,它必須同步進行。所以這會限制你的攔截能力:你可以在異步方法之前或之後注入同步代碼,在異步方法之後注入異步代碼。在異步方法之前無法注入異步代碼。這只是DynamicProxy的一個侷限,一個會糾正非常痛苦的問題(例如,打破全部現有的用戶代碼)。

要做支持的注射種類,您必須改變您的想法。 async的有效心智模型之一是從方法返回的Task表示該方法的執行。因此,要將代碼追加到該方法,您可以直接調用該方法,然後用增強的方法替換任務返回值。

所以,像這樣(的返回類型Task):

protected abstract void PreIntercept(); // must be sync 
protected abstract Task PostInterceptAsync(); // may be sync or async 

// This method will complete when PostInterceptAsync completes. 
private async Task InterceptAsync(Task originalTask) 
{ 
    // Asynchronously wait for the original task to complete 
    await originalTask; 

    // Asynchronous post-execution 
    await PostInterceptAsync(); 
} 

public void Intercept(IInvocation invocation) 
{ 
    // Run the pre-interception code. 
    PreIntercept(); 

    // *Start* the intercepted asynchronous method. 
    invocation.Proceed(); 

    // Replace the return value so that it only completes when the post-interception code is complete. 
    invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue); 
} 

注意,PreIntercept,被攔截的方法,並PostInterceptAsync是在原來的(ASP.NET)環境中的所有運行。

P.S.快速谷歌搜索異步DynamicProxy導致this。不過,我不知道它有多穩定。

+1

感謝您的鏈接我會檢查出來,我會給你的例子。我一直在尋找這個問題的答案。 –

相關問題