2016-09-19 5 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()

私たちは本当にちょうどいいからあるスレッドを別のスレッドにウェーティングする。私は本当に

async Task IInterceptor.Intercept(IInvocation invocation) 

void IInterceptor.Intercept(IInvocation invocation) 

の署名を変更し、Task.RunまたはTask.Factoryを取り除くだけ作るのが大好きだこと:

await InterceptAsync(invocation, taskWrapper); 

問題Castle.DynamicProxy IInterecptorはこれを許可しません。私は本当に迎撃を待っています。私はすることができます。結果は、私は呼び出している非同期呼び出しのポイントは何ですか?待つことができなくても、私はこのスレッドの実行をもたらすことができるという利点を失います。私は彼らのDynamicProxyのためにCastle Windsorについていませんので、私はこれを行う別の方法を探しています。私たちはUnityを検討しましたが、AutoFacの実装全体を置き換えるつもりはありません。

ご協力いただければ幸いです。

答えて

3

私たちが実際に別のスレッドのために1つのスレッドを交換しているので、これはすべて悪いことです。

真。また、StartNewバージョンはメソッドが完了するのを実際に待っていないためです。それは最初のawaitまで待つだけです。しかし、Unwrap()を追加して完全なメソッドを待つようにすれば、デッドロックが発生する可能性が強く疑われます。

問題はCastle.DynamicProxy IInterecptorがこれを許可しないことです。

IInterceptorは、それが同期進まなければならない設計上の制限を持っています。つまり、非同期メソッドの前後に同期コードを挿入し、非同期メソッドの後に非同期コードを挿入することができます。非同期メソッドの前に非同期コードを挿入する方法はありません。これはDynamicProxyの制限です。修正するのは非常に苦しいでしょう(例えば、すべてのユーザコードがのように)。

である注射の種類を行うには、少し考えを変えなければなりません。 asyncの有効なメンタルモデルの1つは、メソッドから返されたTaskがそのメソッドの実行を表すことです。したがって、そのメソッドにコードを追加するには、メソッドを直接呼び出してから、タスクの戻り値を拡張された値に置き換えます。

ので、(Taskの戻り値の型のため)このような何か:PreIntercept、傍受方法、及びPostInterceptAsyncは、元の(ASP.NET)のコンテキスト内のすべての実行されていること

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

注意。

P.S.非同期DynamicProxyをGoogleですばやく検索した結果、thisになりました。私はそれがどれほど安定しているかは分かりません。

+1

リンクをありがとうございます。私はそれをチェックして、あなたの例をあげます。私はこれに対する答えを数日間探してきました。 –

関連する問題