ASP.NET MVC(异步)CurrentCulture不在Controller和View之间共享

我有一个针对.NET Framework 4.7.1的ASP.NET MVC 4应用程序,如果操作包含异步调用,则存在Controller和View之间不共享文化的问题。

我正在引用NuGet包Microsoft.AspNet.Mvc 5.2.3(可以在5.2.4中复制)。

这是Controller中的代码:

 public class CulturesTestController : Controller { public async Task Index(string value) { Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("fi-FI"); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fi-FI"); var model = new CulturesContainer { CurrentCulture = Thread.CurrentThread.CurrentCulture, CurrentUICulture = Thread.CurrentThread.CurrentUICulture, CurrentThreadId = Thread.CurrentThread.ManagedThreadId }; Log.Write(Level.Info, "CurrentUICulture - Before Await - " + "CurrentCulture: " + $"{Thread.CurrentThread.CurrentCulture}, " + "CurrentUICulture: " ${Thread.CurrentThread.CurrentUICulture} -> "+ "ThreadId: " + $"{Thread.CurrentThread.ManagedThreadId}"); await GetAwait(); Log.Write(Level.Info, "CurrentUICulture - After Await - " + "CurrentCulture: " + $"{Thread.CurrentThread.CurrentCulture}, " + "CurrentUICulture: " + $"{Thread.CurrentThread.CurrentUICulture} -> " + "ThreadId: " + $"{Thread.CurrentThread.ManagedThreadId}"); return View("Index", model); } public class CulturesContainer { public CultureInfo CurrentCulture { get; set; } public int CurrentThreadId { get; set; } public CultureInfo CurrentUICulture { get; set; } } private async Task GetAwait() { await Task.Yield(); } } 

这是View中的代码:

 @using System.Globalization @using System.Threading @model CultureTest.Controllers.CulturesTestController.CulturesContainer @{ Layout = null; }    title   
InitialCurrentCulture = { } -- InitialCurrentUICulture = { } -- InitialThreadId = { }
ActualCurrentCulture = { } -- ActualCurrentUICulture = { } -- ActualThreadId = { }

日志如下:

 20180320-12:04:25.357-12 -INFO -CulturesTestController+d__0: CurrentUICulture - Before Await - CurrentCulture: fi-FI, CurrentUICulture: fi-FI -> ThreadId: 12 20180320-12:04:25.357-8 -INFO -CulturesTestController+d__0: CurrentUICulture - After Await - CurrentCulture: fi-FI, CurrentUICulture: fi-FI -> ThreadId: 8 

网页显示:

 InitialCurrentCulture = { fi-FI } -- InitialCurrentUICulture = { fi-FI } -- InitialThreadId = { 12 } ActualCurrentCulture = { en-US } -- ActualCurrentUICulture = { en-US } -- ActualThreadId = { 9 } 

.NET Framework <4.6中存在一个问题,已在4.6中修复,但似乎问题仍然存在于MVC中。

如果线程是执行基于任务的异步操作的线程池线程,并且应用程序面向.NET Framework 4.6或更高版本的.NET Framework,则其UI文化由调用线程的UI文化决定。 (来源https://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.currentuiculture(v=vs.110).aspx )

我没有正确处理CurrentCulture ,或者它应该如何工作? 对于.NET Framework 4.x,我找不到与此问题相关的任何post。

我没有正确处理CurrentCulture,或者它应该如何工作?

你不是。 进入异步操作方法之前 ,需要设置当前UI线程的文化。 如果您在异步方法内部设置文化,则它对UI线程没有影响(如您所发现的那样)。

但是除了异步问题之外,在MVC的应用程序生命周期中 ,在动作方法中设置文化是太晚了,因为MVC的一些重要的文化敏感特性(即模型绑定)在该点之前运行。

在生命周期中尽早设置文化的一种方法是使用授权filter,该filter确保在模型绑定发生之前设置文化。

 using System.Globalization; using System.Threading; using System.Web.Mvc; public class CultureFilter : IAuthorizationFilter { private readonly string defaultCulture; public CultureFilter(string defaultCulture) { this.defaultCulture = defaultCulture; } public void OnAuthorization(AuthorizationContext filterContext) { var values = filterContext.RouteData.Values; string culture = (string)values["culture"] ?? this.defaultCulture; CultureInfo ci = new CultureInfo(culture); Thread.CurrentThread.CurrentCulture = ci; Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name); } } 

并在全球注册:

 public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new CultureFilter(defaultCulture: "fi-FI")); filters.Add(new HandleErrorAttribute()); } } 

上面的示例假设您具有设置路由以将culture添加为类似于此答案的路由值,但您可以设计filter以根据需要从其他位置获取文化。

注意:我意识到这个问题不是关于.NET Core,但正如@GSerg在评论中指出的那样, 同样的问题被报告为ASP.NET Core的一个错误,并且它被by design关闭by design 。 他们得出的结论是一样的:

在资源filter内设置文化将同时应用于操作和视图。