ASP.NET CurrentUICulture ведет себя по-разному в задачах и в результатах WebAPI с задержкой - PullRequest
0 голосов
/ 27 июня 2018

Я работаю над проектом ASP.NET Web API, в котором у меня возникла проблема, когда дочерние Task, которые работают асинхронно, не наследовали текущую культуру родительского потока; то есть даже после установки Thread.CurrentThread.CurrentUICulture в конструкторе контроллера любой Task, созданный в действии, имел инвариантную культуру по умолчанию, если я не установил его отдельно. Кажется, это исправлено в framework> 4.0 и работало нормально после обновления целевой платформы с 3.5.2 до 4.6.2 , как упоминалось здесь .

Начиная с приложений, нацеленных на .NET Framework 4.6, культура вызывающего потока наследуется каждой задачей, даже если задача выполняется асинхронно в потоке пула потоков.

Теперь, когда культурное наследование в Задачах работает должным образом, я столкнулся с еще одной странной проблемой, которой не было , отсутствующей в 4.5.2 . Если результат действия WebApi имеет тип IEnumerable, то в нем будет культура по умолчанию. Может быть, проблему можно было бы лучше объяснить, используя пример кода:

public SampleController()
{
    System.Threading.Thread.CurrentThread.CurrentUICulture =
        new System.Globalization.CultureInfo("ar-AE");
}

[Route]
public IHttpActionResult Get()
{
    IEnumerable<Employee> employeesDeferred = BuildEmployees(true);
    return Ok(employeesDeferred);
}

private IEnumerable<Employee> BuildEmployees(bool isDeferred)
{
    var employees = Enumerable.Range(0, 2)
        .Select(count => new Employee
        {
            Id = count++,
            Culture = Thread.CurrentThread.CurrentUICulture.Name
        });

    if (isDeferred) { return employees; }

    return employees.ToList();
}

Обратите внимание, что я устанавливаю культуру пользовательского интерфейса как "ar-AE" в конструкторе. PFB результаты:

  • framework 4.5.2 - Culture всех сотрудников будет ar-AE, как и ожидалось
  • framework 4.6.2 - Culture всех сотрудников будут в США (по умолчанию)
  • framework 4.6.2, выполнив .ToList() в результате (вызов BuildEmployees(false) вместо BuildEmployees(true) в приведенном выше коде) - Culture всех сотрудников будет ar-AE, как и ожидалось

Я провел некоторое исследование и увидел ответы, в которых предлагается установить переключатель NoAsyncCurrentCulture, например, добавив переключатель в настройках приложения <add key="appContext.SetSwitch:Switch.System.Globalization.NoAsyncCurrentCulture" value="true" /> - но это будет своего рода откат к поведению, которое мы имели в <4.0 и вызовет проблему с наследованием культуры в Задачах, как указано в первом параграфе. </p>

Чего мне не хватает?

Обновление: проверено, что культура контекста не наследуется в потоке, где в Web API происходит сериализация (JSON / XML).

1 Ответ

0 голосов
/ 28 июня 2018

Как уже упоминалось в этом вопросе, UICulture вызывающего потока не передавалось потоку, выполняющему сериализацию. Чтобы убедиться в этом, я создал пользовательский JsonConverter (NewtonSoft) и добавил в качестве конвертера по умолчанию в мой WebAPI и проверил UICulture в методе WriteJson - как и ожидалось, UICulture, который я установил, был переопределен / потерян, и этот поток имел инвариантная культура.

Я не хочу слишком много переигрывать, переписывая весь JsonConverter и, следовательно, не продолжил; вместо этого мне нужно было принудительно сериализовать ответ API в контексте самого действия (где культура пользовательского интерфейса установлена ​​правильно). И то же самое было достигнуто с помощью пользовательского фильтра ниже, где я назвал LoadIntoBufferAsync() содержимого ответа:

    /// <summary>
    /// BUG Description and History:
    /// We had an issue where the child Tasks/async operations were not inheriting the the current culture of the parent thread;
    /// i.e., even after setting the Thread.CurrentThread.CurrentUICulture in the controller constructor, any Task created within the action was having the invariant culture by default 
    /// unless it is set it separately. This seems to have fixed in framework > 4.0 and was working fine after upgrading the target framework from 3.5.2 to 4.6.2
    /// ref - https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming
    /// But with this fix in framework, there introduced an unnoticed? bug where in the UI culture do not flow from the calling thread's context to the NewtonSoft Json Serializer write
    /// And hence, any deferred serialization (like IEnumerable) will run under the Invariant Culture (en-US) thus ending up picking the english resource file instead of the culture set for the thread.
    /// This works fine in framework 3.5.2 or if we enable the "Switch.System.Globalization.NoAsyncCurrentCulture" switch in 4.6.2. But using the switch will diable the flow of culture in async operations and will end up having the issue
    /// we had in 3.5.2.
    /// Inorder to fix this issue, the workaround is to force the serialization happen in the action context itself where the thread UI culture is the one we have set.
    /// </summary>
    public class ForceSerializeHttpContentFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
            filterContext.Response.Content.LoadIntoBufferAsync().Wait();
        }
    }

Не уверен, что это правильное решение; жду больше ответов от сообщества. Спасибо.

...