与WebAPI异步时保留HttpContext(中等信任)

时间:2012-11-26 02:17:04

标签: asp.net .net asynchronous asp.net-web-api async-await

我正在构建一组ASP.Net托管的WebAPI服务,这些服务必须使用一个很大程度上依赖于HttpContext.Current的旧库。我无法确保在参与异步调用的所有方法中保留上下文。我在下面的代码中尝试了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。我没有成功地使用我尝试过的任何内容进行“In Task”调用。另外在我的一些测试线程中,id永远不会变化,这表明我已经有效地删除了该方法的异步性。如何确保在每次调用TestOutput时保留culture和HttpContext.Current,并且代码可以在不同的线程上自由运行?

在闭包中捕获HttpContext.Current然后再次设置它对我来说不起作用,因为我需要支持Medium Trust,它会在调用HttpContext.Current setter时抛出安全异常。

2 个答案:

答案 0 :(得分: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");
});

答案 1 :(得分:6)

只要您await任务,就会保留上下文。

您所看到的是线程池任务没有上下文(Task.RunTaskFactory.StartNew,或者BackgroundWorkerThread或{{1} })。这是正常的和预期的。

因此,不要使用线程池任务。您的示例代码似乎想要使用Delegate.BeginInvoke的多个线程进行并行处理,这是不可能的。

如果需要,您可以执行并发HttpContext方法,但这要求您的async实际上可以是Thread.Sleep方法,而不是基于CPU的方法:

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