1

Setting the culture settings per request should be easy, right? Well it isn't because you set the culture settings on a thread not a request and apparently ASP.NET doesn't always execute the whole request on a single thread. Sure I could use DefaultThreadCurrentCulture, but that would set the culture on all threads, not just the ones used by the specific request.

So am I missing something here or what? Because when I set the culture in Global.asax's Application_AcquireRequestState() function (so it executes once at the beginning of each request) for some reason I have property display names popping up in multiple languages and when I dug deeper, I found out that in the Display Name Attribute's constructor the CurrentCulture and CurrentUICulture settings are sometimes completely different from what I set in Global.asax.

I'm sure that the problem isn't in my customized display name attribute, but you can see it below anyway:

public class DDisplayName : DisplayNameAttribute
    {
        public DDisplayName(string resourceId)
            : base(GetAttributeName(resourceId))
        {  }

        private static string GetAttributeName(string resourceId)
        {
            var lang = System.Threading.Thread.CurrentThread.CurrentCulture.LCID + "|" + System.Threading.Thread.CurrentThread.CurrentUICulture.LCID;

            ResourceManager resMan = new ResourceManager(typeof(Strings));

            string result = resMan.GetString(resourceId);

            return result == null ? "Resource string: " + resourceId + " not found" : result+" "+lang;

        }
    }

As you can see I even added the LCIDs do the display name so I can see them on the screen, and the Culture Settings are different.

EDIT: I also added timestamps and thread IDs to the debugging output above like this.

var lang = System.Threading.Thread.CurrentThread.CurrentCulture.LCID + "|" + System.Threading.Thread.CurrentThread.CurrentUICulture.LCID;
lang += "(" + System.Threading.Thread.CurrentThread.ManagedThreadId + " | "+DateTime.Now.ToString("HH:mm:ss:fff")+")";

It turns out that it persists the DDisplayName attribute instance between requests...huh? Basically, when Html.LabelFor() runs it looks through the metadata for the property to find the display name. When the metadata provider sets the meta data it finds the displayname from the PropertyDescriptor instance which holds a list of attributes for the property. And in this list, the attribute instance for some reason is an old one from a previous request (I know that because of the timestamp I add to the displayname in the constructor). And so, after I switch languages it still uses the old attribute instance that I had when I had set the previous language. So what's happening? Why doesn't it create a new DDisplayName object instance on each request?

EDIT 2: I have no idea why it keeps cashing the attribute instances so I just wrote my own MetaDataProvider that inherits from DataAnnotationsModelMetadataProvider and made sure it finds the real (latest) display name.

Fabis
  • 1,932
  • 2
  • 20
  • 37
  • Not sure why you want to do it per request to be honest... anyhow why don't you set the current thread culture using an actionfilter? – Paul Zahra Jun 30 '15 at 11:15
  • Because the user's language of choice is set in a cookie, which is read on each request. And nothing would change if I'd set it in an action filter, because the AcquireRequestState event acts essentially the same way. And either way I already tried it. – Fabis Jun 30 '15 at 11:16
  • btw "Cultures are applied on the thread level in .NET and ASP.NET assigns these culture settings on the active request thread which remains active for the lifetime of a typical ASP.NET request." – Paul Zahra Jun 30 '15 at 11:18
  • Well, apparently not. To me it looks like it's switching threads around during a single request and I've read that elsewhere too. – Fabis Jun 30 '15 at 11:20
  • Have a read of how Rick Strahl does it... he gives some nice code too.. .and sets it to run in an overloaded Initialize method... scroll down a bit to see the MVC solution... http://weblog.west-wind.com/posts/2014/Mar/27/Auto-Selecting-Cultures-for-Localization-in-ASPNET – Paul Zahra Jun 30 '15 at 11:21
  • That code is pretty much what I'm doing already. Anyway, it turns out that when the attribute list for the property is read, it reads an older version of the attribute from a previous request...how does that even work? I updated the original post with more details. – Fabis Jun 30 '15 at 11:34
  • Could it be because the AcquireRequestState isn't firing as you think? Have you tried setting the culture in Application_BeginRequest() ? If you are setting the culture in the session you are using the more correct event... i.e. BeginRequest runs earlier in the lifecycle and might run before the session object is available. – Paul Zahra Jun 30 '15 at 11:48
  • Even if that were the case, it wouldn't explain why the object instances persist between requests. – Fabis Jun 30 '15 at 11:51
  • I'm unsure... but you state "so it executes once at the beginning of each request", Acquire is actually the 12th event to occur... BeginRequest is the 3rd... https://msdn.microsoft.com/en-us/library/ms178473%28v=VS.100%29.aspx – Paul Zahra Jun 30 '15 at 11:57
  • I'm guessing that your problem with persistence may lay with the caching of the page, i.e. outputcache... it could possibly be looking at an 'older' version of the page, i.e. before you set the culture. – Paul Zahra Jun 30 '15 at 12:06
  • Cache-Control:no-cache, no-store in the headers and it's still happening. – Fabis Jun 30 '15 at 12:20
  • Also what are your web.config settings for globalization ? – Paul Zahra Jun 30 '15 at 12:20
  • I had a element in there before, but I removed it. – Fabis Jun 30 '15 at 12:21
  • It may seem strange but try setting – Paul Zahra Jun 30 '15 at 12:23
  • Also try this brute force cache disable http://dotnet.dzone.com/articles/output-caching-aspnet-mvc if only as a test. – Paul Zahra Jun 30 '15 at 12:23
  • That would read the settings from the browser's accept-language variable, which is not what I'm after. And yea I already did brute-force cache disable, as I mentioned above. – Fabis Jun 30 '15 at 12:24
  • P.S. no-cache can mean different things in different browsers... http://stackoverflow.com/questions/1046966/whats-the-difference-between-cache-control-max-age-0-and-no-cache – Paul Zahra Jun 30 '15 at 12:31
  • I'm on Chrome and I did try various cache settings, including max-age=0 – Fabis Jun 30 '15 at 12:34

0 Answers0