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内设置文化将同时应用于操作和视图。