繁体   English   中英

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

[英]ASP.NET MVC (Async) CurrentCulture is not shared between Controller and 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<ActionResult> 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;
}

<!DOCTYPE html>

<html>
<head>
    <title>title</title>
</head>
<body>
    <div>
        InitialCurrentCulture = {
        <label>@Html.Raw(Model.CurrentCulture)</label>
        }  --
        InitialCurrentUICulture = {
        <label>@Html.Raw(Model.CurrentUICulture)</label>
        }  --
        InitialThreadId = {
        <label>@Html.Raw(Model.CurrentThreadId)</label>
        }
        <br />
        ActualCurrentCulture = {
        <label>@Html.Raw(CultureInfo.CurrentCulture)</label>
        }  --
        ActualCurrentUICulture = {
        <label>@Html.Raw(CultureInfo.CurrentUICulture)</label>
        }  --
        ActualThreadId = {
        <label>@Html.Raw(Thread.CurrentThread.ManagedThreadId)</label>
        }
    </div>
</body>
</html>

日志如下:

20180320-12:04:25.357-12   -INFO -CulturesTestController+<Index>d__0: 
    CurrentUICulture - Before Await - 
    CurrentCulture: fi-FI, CurrentUICulture: fi-FI -> ThreadId: 12
20180320-12:04:25.357-8    -INFO -CulturesTestController+<Index>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,我找不到与此问题相关的任何帖子。

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

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

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

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

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添加为类似于此答案的路由值,但您可以设计过滤器以从其他位置获取文化(如果需要)。

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

在资源过滤器中设置区域性将同时应用于操作和视图。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM