ASP.NET MVC 5 localization resources freeze and do not change language despite changing CurrentThread.CurrentCulture

戏子无情 提交于 2020-01-03 03:15:35

问题


I have an ASP.NET MVC 5 WEB Application running under a website in IIS 8 where I need to change language programmatically at runtime by a user preference that I read from a DB and store in a session variable, and this value can change during runtime with a dropdown.

The problem is language resources sometimes do not change, despite the CurrentCulture does and the worse thing is they freeze at an application level making that all the other user sessions to freeze to that language too and will show only in all application sessions the same language in which is stuck regardless their preference or if they even try to change their language at runtime which when is working normally forces CurrentThread.CurrentCulture to change.

I use a hidden text in the views to track what the CurrentThread.CurrentCulture was and is changing as expected so the problem must be some place else.

This is the razor block I use to inspect CurrentCulture

<div style="display:none; visibility: hidden;">Thread  
@System.Threading.Thread.CurrentThread.CurrentCulture</div>

And at runtime when the language or resx are freezed/stucked at an application level the value is indeed the correct and selected but it does not match the language being displayed in the views and it wont work normally until I make for example a change in the web.config or restart the application and then it works fine for a while but do not last too much time.

<div style="display:none; visibility: hidden;">Thread en-US</div>

The problem is that this happens at an application level for all user sessions and the CurrentCulture is actually changing as expected when the application is working normally and whenever is restarted this does not happen but somehow every certain time something triggers during user sessions to freeze of the language of all current sessions and even new sessions.

I have tried placing the change of culture logic in the razor views, action filters and a base controller too but that did not solve the problem, previously I had a base controller which all my controller inherited and logic at Global.asax.cs to change and determine culture.

My current approach was to remove the base controller and using in both Global.asax.cs Application_AcquireRequestState (I´ve tried too using Application_BeginRequest but somehow is too earlier in lifecycle) and an Action Filter.

This is in Global.asax.cs

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        if (Context.Session != null && Context.Session["CultureName"] != null)
        {
             string cultureName = Context.Session["CultureName"].ToString();

             CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);

            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;

        }
    }

And my Action Filter like this

public class CultureActionAttribute : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        string cultureName = null;

        string tenant = filterContext.RouteData.Values["Tenant"] as string;
        if (!String.IsNullOrWhiteSpace(tenant)) 
        {
            TenantService tenantService = new TenantService(tenant);

            cultureName = CultureHelper.GetImplementedCulture(tenantService.Tenant.LanguageCulture);

            tenantService.Unit.Dispose();

        }
        //Logic to override cultureName value by getting the user preference language

        CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);

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

And I added it at FilterConfig

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new CultureActionAttribute());
        //
    }

I even tried to use ClearCachedData when is the user changes culture.

Thread.CurrentThread.CurrentCulture.ClearCachedData();

I think this might be relating to caching or pool recycling but I am out of ideas of what to try next.

I am not using any globalization tag at web.config.

The value in traced in views of CurrentThread.CurrentCulture is always correct what is wrong is the freezing with the use of resources.

Resources are consistent with the practice of placing them in a separate, dedicated standard folder and to use PublicResXFileCodeGenerator to make them public to able to compile and be accessible to Controllers, Views, etc

Resources folder

Can anyone please provide me another choice to try or thought about what might I be doing wrong or what else can I try to make resources to work consistently.


回答1:


I believe what is happening here is that you are setting the culture in a different thread than where you are actually using it. You are setting the culture in an asynchronous method BeginExecuteCore, which might not be consistently transferring the culture to your application's UI working thread. This would explain the intermittent behavior - you are probably seeing it "freeze" during heavy load when the application threads are running slowly.

Never use a base controller in MVC - this is never a good idea. Instead, for cross-cutting concerns such as localization, you can use a global filter. This not only side-steps your async issue, it promotes a more loosely-coupled design. Here is an example of a filter that gets the culture from the current route:

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);
    }
}

Usage

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

I would also argue that using session state for localization is not a great idea - after all, you are left in a black hole when the session expires.

Locales are not personalization. Locales are content. The correct way to serve unique content is to give it a unique URL. If you do that, selecting the culture is as simple as selecting a new URL and it will "stick" automatically because MVC re-uses route values. It will never expire even if the user sends the URL to someone else. And most importantly, all the work you have done translating your site into multiple languages will be visible by search engines, so it will have a much greater payoff.

See ASP.NET MVC 5 culture in route and url for a working example of a URL-based approach.




回答2:


The only way I could ensure to get the correct resources was to make a work around to override directly over the resources the CurrentThreadUICulture property for all resource lookups at the end of my CultureFilter

Resources.Resource.Culture = cultureInfo;

And this did the trick!, taking advantage to have only single files as global resources, so everything is working as expected now, but I understand this would be inconvenient for multiple resources files and namespaces. Nevertheless there was no other way for me in which I could ensure language changing would work and wouldn't freeze after trying to use a base controller, and Iauthorization actionfilter attribute, or using Application_AcquireRequestState at Global.asax.cs where none of the three methods could always change the UI language correctly because of the freezing despite Thread.CurrentThread.CurrentUICulture was always changed correctly with the three approaches.



来源:https://stackoverflow.com/questions/38274406/asp-net-mvc-5-localization-resources-freeze-and-do-not-change-language-despite-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!