控制器和视图之间不共享ASP.NET MVC(异步)CurrentCulture [英] ASP.NET MVC (Async) CurrentCulture is not shared between Controller and View

查看:58
本文介绍了控制器和视图之间不共享ASP.NET MVC(异步)CurrentCulture的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个针对.NET Framework 4.7.1的ASP.NET MVC 4应用程序,问题是如果操作包含异步调用,则Controller和View之间不会共享区域性.

I have an ASP.NET MVC 4 application that is targeting .NET Framework 4.7.1, with the problem that the culture is not shared between Controller and View, if the action contains async calls.

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

I am referencing the NuGet package Microsoft.AspNet.Mvc 5.2.3 (and can be reproduced in 5.2.4).

这是控制器中的代码:

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中的代码:

And this is the code in 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中.

There was an issue in .NET Framework < 4.6 that was fixed in 4.6, but it seems that the problem still persists in MVC.

如果该线程是一个正在执行基于任务的异步操作的线程池线程,并且该应用程序以.NET Framework 4.6或.NET Framework的更高版本为目标,则其UI文化取决于该UI的UI文化.调用线程. (来源 https://msdn.microsoft.com/zh-CN/library/system.globalization.cultureinfo.currentuiculture(v=vs.110).aspx )

我没有正确处理CurrentCulture,还是应该这样工作?我找不到适用于.NET Framework 4.x的与此问题相关的任何帖子.

Am I not correctly handling the CurrentCulture, or this is how it is supposed to work? I couldn't find any post related to this issue, for .NET Framework 4.x.

推荐答案

我没有正确处理CurrentCulture,还是应该这样工作?

Am I not correctly handling the CurrentCulture, or this is how it is supposed to work?

您不是.必须先设置当前UI线程的区域性,然后再进入异步操作方法.如果您在异步方法内部设置区域性,则它对UI线程没有影响(如您所见).

You are not. The culture of the current UI thread needs to be set before going into your async action method. If you set the culture while you are inside of an async method, it has no effect on the UI thread (as you have discovered).

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

But async problems aside, it is too late in the application lifecycle of MVC to be setting the culture inside of an action method, since some important culture-sensitive features of MVC (i.e. model binding) run before that point.

在生命周期中尽早设置区域性的一种方法是使用授权过滤器,以确保在模型绑定发生之前就已设置区域性.

One way to set the culture early enough in the lifecycle is to use an authorization filter, which ensures the culture is set before model binding takes place.

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添加为类似于此答案的路由值,但您可以设计一个过滤器,以根据需要从其他地方获取文化.

The above example assumes that you have setup routing to add the culture as a route value similar to this answer, but you could design a filter to get the culture from somewhere else, if desired.

注意::我意识到这个问题不是关于.NET Core的,而是@GSerg在评论中指出的,这个

NOTE: I realize this question is not about .NET Core, but as @GSerg pointed out in the comments, this same issue was reported as a bug for ASP.NET Core, and it was closed as by design. The conclusion they came to was the same:

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

这篇关于控制器和视图之间不共享ASP.NET MVC(异步)CurrentCulture的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆