locked
Serializer runs in the new thread? RRS feed

  • Question

  • User-206128131 posted

    I have a Get (int Id) method in my controller that returns class instance.

    Instance is created correctly(i can see that in the debugger) but on attempt to return

    return classInstance;

    my code crashes since it runs in the new thread and therefore setting culture to "en-US" .

    Previously I have set default culture to "en-CA" which works beautifully on JsonConvert.SerializeObject(user,Formatting.Indented,settings).

    How to fix the issue without modifying legacy code on classInstance. Is there a way to set culture for all threads within current request.

    Or if you have some other idea.

    Thursday, March 22, 2018 5:55 PM

All replies

  • User753101303 posted

    Hi,

    Ah, each time I see a question about that, it make me think this is something I would like to check once for all. AFAIK the ASP.NET synchronization context should take care of keeping the same culture. You may perhaps have this problem if you explicitely start new threads (if I remember there is something new in 4.6 to help for that).

    You are using just using existing async APIs or you launch a new thread yourself ?

    BTW : It seems to me json serialiazation shouldn't be sensitive to the culture. You may want to be explicit about the exact problem you have. Which error is this and on which line of code does it happen? Could ie be caused actually by something else ?

    Thursday, March 22, 2018 6:34 PM
  • User-206128131 posted

    i did not open any threads  explicitely. I let the server (framework) handle multi-threading.

    I use web.api core 2.0. I have a controller method that gets the instance of class. instance is a full graph and i can confirm that is correctly created.

    code crashes when I try to return instance.

    I think at that point while framework tries to serialize object it open the new thread with default culture.

    Thursday, March 22, 2018 6:45 PM
  • User-206128131 posted

            [HttpGet("{id}")]

            public UserModel Get(int id)

            {

                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-CA");

                UserModel user = UserService.GetUserById(id);

                return user;

            }

    Thursday, March 22, 2018 6:58 PM
  • User753101303 posted

    You have a specialized forum at https://forums.asp.net/1255.aspx/1?ASP+NET+Core that could be better. I believe this one is rather about the ASP.NET 4.x web api.

    Also be explicit about what happens. Not sure to get which kind of "crash" you are seeing or what makes you think it crashes because of a culture issue. It's likely best to just tell what happens. Do you have an exception ?

    Thursday, March 22, 2018 7:43 PM
  • User-206128131 posted

    yes

    one of underlying classes has localized attributes. Culture is pulled from CurrentThread which in this case is set to be a defalut one "en-US" instead as set in the code several lines above to "en-CA".

    since thread is set to "en-US" lazy loaded attribute crashes since it can not get correct item from collection. 

    public static int GetThreadCultureId()

                  {

                     CultureModel cultureModel = null;

                     string code = System.Threading.Thread.CurrentThread.CurrentCulture.Name;

                      try {

                              List<CultureModel> cultures = GetCulturesList();

                              cultureModel = cultures.Where(culture => culture.Code == code).First();

                          }

                     catch (Exception e)

                      {

                          throw new CoreExceptions.ObjectNotFoundInDatabaseException(string.Format("The requested culture (Code = \"{0}\") was not found.", code));

                       }

                        return cultureModel.Id;

                   }

    Thursday, March 22, 2018 7:50 PM
  • User-474980206 posted

    in asp.net core, you should not set the thread culture. this is because, unlike the previous version, the culture is not added the task context, and set on task switches. in asp.net core, DI is used to inject a culture into the request and code is supposed to use the passed culture.

    if you have a library which uses the thread culture, and you can not change it, the fix is simple, just create a new thread, set the new threads culture, call the library on this new thread.

    await Task.Run(() => 
    {
       // set thread culture
       // call legecy code
    });
    


    Friday, March 23, 2018 1:52 AM
  • User-206128131 posted

    Thanks Bruce,

    but I don't think this is issue here. Please take a look at Get method above. it has only 3 lines

                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-CA");

                UserModel user = UserService.GetUserById(id);

                return user;

    exception is thrown on return user because serializer tries to serialize user. It appears that serializer runs in its own thread therefore having default culture = "en-US". this is not working for me since one of underlying classes uses curenttread.cultureName in one of localized class attributes to identify current culture and set that attribute for appropriate culture. 

    In our application we have only 2 cultures en-CA and en-FR. 

    I hope this clarifies the issue. 

    Friday, March 23, 2018 2:19 AM
  • User753101303 posted

    It fails really on return user rather than on First ???

    IMHO it's best to just tell which exception you have so that you don't have an interpretation on your side, one on our side making hard to figure out the exact problem you have on your side.

    Also if your code fails in The requested culture (Code = \"{0}\") was not found." you'll still generate a message telling that en-US is not found (my GUESS is that this is the message you currently have ?)

    Friday, March 23, 2018 8:34 AM
  • User-206128131 posted

    i think this will fail the same way. 

    public UserModel Get(int id)
    {
      System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-CA");
      UserModel user = new UserModel();
      user.Id = 7;
      return user;
    }

    public class UserModel
    {
      public int Id { get; set; }
      public string Country{
      get
      {
        string code = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
        List<string> clt = new List<string>();
        clt.Add("en-CA");
        clt.Add("fr-CA");
        clt.Where(c => c == code).First(); // it will fail here since code == "en-US" from current thread. system is designed to work only with en-ca and en-fr.
        //.....
        }
      }
    }

    Friday, March 23, 2018 12:57 PM
  • User753101303 posted

    Ah. I would never have guessed your design. Both your web api and serialization code are called from a higher level so I believe the culture should be set elsewhere and earlier. I would have to give a closer look.

    Before you really can't change this design? Basically you just assign a model property indirectly by changing the culture for the thread. If you want to keep the current Country as read only you could perhaps create a SetCultureName method. Your model is then  unchanged but at least you can get rid of depending on the current thread culture for what should be really a model property.

     

    Friday, March 23, 2018 2:18 PM
  • User-206128131 posted

    i see your point. 

    UserModel its a legacy class. I would rather not change it if i don't have to since it might have impact to other projects or system parts that relay on that class.

    in asp.net we had global.ascx that every request goes trough. is it possible to set request context culture to "en-FR" for eg. in core.net web api

    beside 

    even 

    mvcBuilder.AddJsonOptions(
    options =>
    {
    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    options.SerializerSettings.Culture = new System.Globalization.CultureInfo("en-CA");

    });

    its not working in this case.

    Friday, March 23, 2018 4:37 PM
  • User753101303 posted

    To get the full picture you are always showing code where "en-CA" is hardcoded while you told you have support for both en-CA and "en-FR" (I guess you meant "fr-CA" ?)

    How this decision is done ? The culture is part of the url maybe ? A prominent idea of ASP.NET Core is to configure your app once for all and if I remember you can use (or write) code that ASP.NET Core can call as well to ge the culture as needed.

    Ah found https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization#implement-a-strategy-to-select-the-languageculture-for-each-request

    So I would try to configure the app wiht whatever fit the current decision you are taking to define the current culture. I then hope that ASP.NET Core will call this code as needed to set the thread culture and that you'll perhaps then  see the correct culture is used as well for the final serialization step.

    Friday, March 23, 2018 5:14 PM
  • User-206128131 posted

    yes i meant fr-CA sorry :).

    the legacy application sets culture in begin_request event in global.ascx - reading it from user preference cookie. 

    since we don't have such request handler in core.net i was hoping that there is an alternative.

    so depending on user preference currentThread culture will be set for the duration of the request. 

    for api this needs to change and i was thinking to send culture preference in header- possibly using Accept-Language header.

    but that is not a point. point is that serializer disregards current culture and it runs under default one. 

    Friday, March 23, 2018 6:27 PM
  • User753101303 posted

    I tried the link I posted earlier and it seems to work fine. So ASP.NET Core uses this middleware that takes the culture from the querystring cookies or http headers (or you could add custom providers if you want). It will then used this as needed to set the correct culture and it works fine even in the serializer...

    Friday, March 23, 2018 8:49 PM