Async WebApi Thread.CurrentCulture Async WebApi Thread.CurrentCulture multithreading multithreading

Async WebApi Thread.CurrentCulture


As Joe pointed out, culture is transferred by the HttpContext in ASP.NET. The way ASP.NET does this is by installing a SynchronizationContext when a request starts, and that context is also used to resume asynchronous methods (by default).

So, there are a couple of ways to approach the problem: you can either write your own SynchronizationContext that will preserve culture by default, or you can explicitly preserve the culture across each await.

To preserve the culture at each await, you can use code from Stephen Toub:

public static CultureAwaiter WithCulture(this Task task) {     return new CultureAwaiter(task); }public class CultureAwaiter : INotifyCompletion{     private readonly TaskAwaiter m_awaiter;     private CultureInfo m_culture;    public CultureAwaiter(Task task)     {         if (task == null) throw new ArgumentNullException("task");         m_awaiter = task.GetAwaiter();     }    public CultureAwaiter GetAwaiter() { return this; }    public bool IsCompleted { get { return m_awaiter.IsCompleted; } }    public void OnCompleted(Action continuation)     {         m_culture = Thread.CurrentThread.CurentCulture;         m_awaiter.OnCompleted(continuation);     }    public void GetResult()     {         Thread.CurrentThread.CurrentCulture = m_culture;         m_awaiter.GetResult();     } }

The SynchronizationContext approach is more complicated but once it's set up, it will be easier to use. I don't know of a good example of an ASP.NET-like context, but a good starting point is my MSDN article.


From .NET 4.5, to set a default culture for all threads, use:

CultureInfo.DefaultThreadCurrentCulture = culture;CultureInfo.DefaultThreadCurrentUICulture = culture;


Thread.CurrentCulture doesn't get synchronized across threads. However, your HttpContext does. You would be better off getting your culture information from your HttpContext directly. You can do something like

public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken){    if (controllerContext.Request.Headers.AcceptLanguage != null &&         controllerContext.Request.Headers.AcceptLanguage.Count > 0)    {        string language = controllerContext.Request.Headers.AcceptLanguage.First().Value;        var culture = CultureInfo.CreateSpecificCulture(language);        HttpContext.Current.Items["Culture"] = culture;        //Thread.CurrentThread.CurrentCulture = culture;        //Thread.CurrentThread.CurrentUICulture = culture;    }    base.ExecuteAsync(controllerContext, cancellationToken); }

and then, in any task you need the culture:

var culture = HttpContext.Current != null ? HttpContext.Current.Items["Culture"] as CultureInfo : Thread.CurrentThread.CurrentCulture;