首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步WebApi Thread.CurrentCulture

异步WebApi Thread.CurrentCulture
EN

Stack Overflow用户
提问于 2013-12-16 00:12:30
回答 3查看 14.5K关注 0票数 42

我有一个自我托管的OWIN托管的Web 项目,为我提供了一些基本的REST方法。

我希望有多语言错误消息,所以我使用BaseController资源文件和一个将Thread.CurrentCultureThread.CurrentUICulture设置为请求的Accept-Language头的Accept-Language

代码语言:javascript
复制
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);

        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }

    base.ExecuteAsync(controllerContext, cancellationToken);
}

这一切都很好,但是如果我让我的控制器方法异步,问题就出现了。

当我在方法中使用CurrentCulture等待时,它可能会在另一个线程中继续,因此我的和CurrentUICulture丢失了。

这是我以前发现这个问题的一个小例子。

代码语言:javascript
复制
public async Task<HttpResponseMessage> PostData(MyData data)
{
    Thread currentThread = Thread.CurrentThread;

    await SomeThing();

    if (Thread.CurrentThread.CurrentCulture != currentThread.CurrentCulture)
        Debugger.Break();
}

我并不总是打破Debugger.Break的线,但大多数时候我是这样做的。

下面是一个实际使用资源文件的示例。

代码语言:javascript
复制
public async Task<HttpResponseMessage> PostMyData(MyData data)
{
    //Before this if I'm in the correct thread and have the correct cultures
    if (await this._myDataValidator.Validate(data) == false)
        //However, I might be in another thread here, so I have the wrong cultures
        throw new InvalidMyDataException(); 
}

public class InvalidMyDataException : Exception
{
    public InvalidMyDataException()
        //Here I access my resource file and want to get the error message depending on the current culture, which might be wrong
        : base(ExceptionMessages.InvalidMyData) 
    {

    }
}

一些附加信息:我有很多这样的异常,它们都会被捕获到一个定制的ExceptionFilterAttribute中,然后创建响应。

因此,在我使用文化之前,总是设置好它是一段很长的代码。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-12-16 01:26:07

正如Joe所指出的,区域性是由HttpContext在ASP.NET中传输的,ASP.NET的方式是在请求启动时安装一个SynchronizationContext,并且这个上下文也用于恢复异步方法(默认情况下)。

因此,有几种方法可以解决这个问题:您可以编写自己的SynchronizationContext来保留默认的区域性,或者可以在每个await中显式地保留区域性。

要保留每个await的区域性,可以使用代码from Stephen Toub

代码语言:javascript
复制
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(); 
    } 
}

SynchronizationContext方法更复杂,但一旦设置完毕,它将更容易使用。我不知道一个类似ASP.NET的上下文的好例子,但是一个很好的起点是my MSDN article

票数 26
EN

Stack Overflow用户

发布于 2014-11-06 15:07:47

在.NET 4.5中,若要为所有线程设置默认区域性,请使用:

代码语言:javascript
复制
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
票数 8
EN

Stack Overflow用户

发布于 2013-12-16 00:34:04

Thread.CurrentCulture不会跨线程同步。但是,您的HttpContext会这样做。您最好直接从HttpContext中获取您的文化信息。你可以做这样的事

代码语言:javascript
复制
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); 
}

然后,在任何你需要文化的任务中:

代码语言:javascript
复制
var culture = HttpContext.Current != null ? HttpContext.Current.Items["Culture"] as CultureInfo : Thread.CurrentThread.CurrentCulture;
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20601578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档