6

HttpContext.Currentに大きく依存する古いライブラリを使用する必要があるASP.NetでホストされるWebAPIサービスのセットを構築しています。非同期呼び出しに参加するすべてのメソッドでコンテキストが保持されるようにするのに問題があります。以下のコードでawait/Task.WaitとTaskScheduler.FromCurrentSynchronizationContext()を使用していくつかのバリエーションを試しました。

    [HttpGet]
    public Task<IEnumerable<string>> ContinueWith()
    {
        Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");  //or another culture that is not the default on your machine
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

        var output = new List<string> { TestOutput("Action start") };

        var task = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                return TestOutput("In Task");
            }).ContinueWith(slowString =>
            {
                output.Add(slowString.Result);

                output.Add(TestOutput("Action end"));
                return output as IEnumerable<string>;
            });

        output.Add(TestOutput("Action Mid"));

        return task;
    }

    private string TestOutput(string label)
    {
        var s = label + " ThreadID: " + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture);
        s += " " + Thread.CurrentThread.CurrentCulture.EnglishName;
        s += HttpContext.Current == null ? " No Context" : " Has Context";
        Debug.WriteLine(s);
        return s;
    }

CurrentCultureがfr-FRであり、TestOutputが呼び出される各ポイントでHttpContext.Currentがnullでないことを確認できるようにしたいと思います。私が試したことは何でも、「タスク中」の呼び出しでそれを行うことに成功していません。また、一部のテストスレッドIDは変化せず、メソッドの非同期性を効果的に削除したことを示しています。TestOutputを呼び出すたびに、カルチャとHttpContext.Currentが保持され、コードがさまざまなスレッドで自由に実行できるようにするにはどうすればよいですか?

クロージャーでHttpContext.Currentをキャプチャしてから再度設定するだけでは、HttpContext.Currentセッターを呼び出すときにセキュリティ例外をスローするMedium Trustをサポートする必要があるため、機能しません。

4

2 に答える 2

8

少し気づいた事実、HttpContext.Currentは書き込み可能です。

var context = HttpContext.Current;
var task = Task.Factory.StartNew(() => {
    HttpContext.Current = context;
    // You may want to set CultureInformation here too.

    return TestOutput("In Task");
});
于 2012-11-26T07:10:34.277 に答える
6

awaitタスクを実行するたびにコンテキストが保持されます。

表示されているのは、スレッドプールタスクのコンテキストがないことです(Task.Run、、、またはTaskFactory.StartNewそのことについては、または)。これは正常であり、予想されます。BackgroundWorkerThreadDelegate.BeginInvoke

したがって、スレッドプールタスクは使用しないでください。サンプルコードは、を含む複数のスレッドで並列処理を実行したいと考えているようですがHttpContext、これは単純に不可能です。

必要に応じて並行asyncメソッドを実行できますが、これには、CPUベースのメソッドではなくThread.Sleep実際にメソッドになることができる必要があります。async

[HttpGet]
public async Task<IEnumerable<string>> Test()
{
  Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
  Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

  var output = new List<string> { TestOutput("Action start") };

  var task = SlowStringAsync();
  output.Add(TestOutput("Action Mid"));
  output.Add(await task);
  output.Add(TestOutput("Action end"));
  return output;
}

public async Task<string> SlowStringAsync()
{
  await Task.Delay(1000);
  return TestOutput("In Task");
}

古いライブラリが制御不能で作成できない場合asyncは、同期的に呼び出す必要があります。async次のような状況では、メソッドから同期メソッドを呼び出すことができます。

[HttpGet]
public async Task<IEnumerable<string>> Test()
{
  Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
  Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

  var output = new List<string> { TestOutput("Action start") };

  output.Add(TestOutput("Action Mid"));
  Thread.Sleep(1000);
  output.Add(TestOutput("Not Really In Task"));
  output.Add(TestOutput("Action end"));
  return output;
}
于 2012-11-26T02:55:44.343 に答える