AsyncController と依存性注入を混在させようとしています。問題の MVC アプリは、非同期 Web サービス呼び出しを介してほぼすべてのデータを取得します。非同期作業を TPL からタスクにラップし、これらのタスクが完了するとコントローラーの AsyncManager に通知します。
これらのタスクの継続で HttpContext に触れなければならない場合があります - Cookie の追加などです。ASP.NET MVC での非同期コントローラーの使用に従ってこれを行う正しい方法は、メソッドを呼び出すことAsyncManager.Sync
です。これにより、HttpContext を含む ASP.NET スレッド コンテキストが現在のスレッドに伝達され、コールバックが実行され、前のコンテキストが復元されます。
ただし、その記事には次のようにも書かれています。
既に ASP.NET の制御下にあるスレッドから Sync() を呼び出すと、未定義の動作が発生します。
コントローラーですべての作業を行う場合、これは問題ではありません。これは、通常、継続でどのスレッドにいる必要があるかを知っているためです。しかし、私がやろうとしているのは、非同期データ アクセスと非同期コントローラーの間に中間層を作成することです。したがって、これらも非同期です。すべてが DI コンテナーによって配線されます。
以前と同様に、呼び出しチェーン内のコンポーネントの一部は、「現在の」HttpContext で動作する必要があります。たとえば、ログオン後、シングル サインオン サービスから返される「セッション」トークンを保存したいとします。それを行うための抽象化が ISessionStore です。応答に Cookie を配置する、または要求から Cookie を取得する CookieSessionStore について考えてみてください。
これで私が見ることができる2つの問題:
- コンポーネントは AsyncManager にアクセスできず、コントローラー内で使用されていることさえ知りません。
- コンポーネントは、どのスレッドから呼び出されているかを認識していないため、理論的には AsyncManager.Sync または同等のものに問題があります。
#1 を解決するために、私は基本的TaskScheduler.FromCurrentSynchronizationContext()
に、リクエストの開始時にグラブするオブジェクトを注入し、HttpContextBase を引数として取り、そのスケジューラーで開始されたタスクを介してアクションを呼び出すことができます。
つまり、私のコンポーネントから、次のようなものを呼び出すことができます:
MySyncObject.Sync(httpContext => /* Add a cookie or something else */);
これに関する問題はまだ確認されていませんが、問題 2 が心配です。AsyncManager
私はとReflector の両方を調べましたがSynchronizationContextTaskScheduler
、それらは同様に動作し、 ASP.NET でコールバックを実行しますSynchronizationContext
。そして、それは私を怖がらせます:)
インライン化されている場合、タスク スケジューラの実装が同期コンテキストを経由するのではなく、直接呼び出すことを確認したとき、少し希望が持てました。残念ながら、これは通常のTask.Start(scheduler)
コード パスでは発生しないようです。むしろ、開始前に待機している場合など、他の状況でタスクをインライン化できます。
だから私の質問は:
- このアプローチでここで問題が発生するでしょうか?
- より良い方法はありますか?
- このシナリオでの同期の有用性は、スレッドセーフでない HttpContext へのアクセスをシリアル化するためだけですか? つまり、代わりにスレッドセーフな HttpContextBase ラッパーを使用できますか (ick)?