5

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つの問題:

  1. コンポーネントは AsyncManager にアクセスできず、コントローラー内で使用されていることさえ知りません。
  2. コンポーネントは、どのスレッドから呼び出されているかを認識していないため、理論的には 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)コード パスでは発生しないようです。むしろ、開始前に待機している場合など、他の状況でタスクをインライン化できます。

だから私の質問は:

  1. このアプローチでここで問題が発生するでしょうか?
  2. より良い方法はありますか?
  3. このシナリオでの同期の有用性は、スレッドセーフでない HttpContext へのアクセスをシリアル化するためだけですか? つまり、代わりにスレッドセーフな HttpContextBase ラッパーを使用できますか (ick)?
4

1 に答える 1

1

スレッドローカルの静的に依存することは、めったに良い考えではありません。はそのメカニズムにHttpContext.Current依存しており、何年も機能してきましたが、現在非同期になっているため、そのアプローチは急速に悪化しています。この static の値をローカル変数として取得し、それを非同期作業で渡す方がはるかに優れているため、常にそれが得られます。たとえば、次のようになります。

public async Task<ActionResult> MyAction() {
    var context = HttpContext.Current;
    await Task.Yield();
    var item = context.Items["something"];
    await Task.Yield();
    return new EmptyResult();
}

またはHttpContext.Current、MVC を使用している場合は、完全に避けてください。

public async Task<ActionResult> MyAction() {
    await Task.Yield();
    var item = this.HttpContext.Items["something"];
    await Task.Yield();
    return new EmptyResult();
}

おそらく、ミドルウェアのビジネス ロジックは特に、HttpContext や ASP.NET ライブラリ内の他のものに依存するべきではありません。したがって、ミドルウェアが (コールバック、インターフェースなどを介して) コントローラーにコールバックして Cookie を設定すると仮定すると、this.HttpContextそのコンテキストにアクセスするために使用できるようになります。

于 2012-12-31T17:28:18.120 に答える