Подтвердить что ты не робот

Это хороший подход для временного изменения текущей культуры потоков?

Я работаю над довольно большим приложением ASP.NET Web Forms, которое в настоящее время используется в основном в Соединенных Штатах. Мы находимся в процессе развертывания его в других частях мира, что, конечно же, означает, что мы в настоящее время работаем над локализацией всех областей приложения. В общем, наш подход заключался в том, чтобы установить текущие текущие свойства CurrentCulture и CurrentUICulture в начале каждого запроса, чтобы поддерживать правильное форматирование и извлечение ресурсов на основе текущего локали пользователя.

В некоторых случаях, однако, нам нужно запустить определенный бит кода, используя культуру, отличную от культуры текущего пользователя. Например, "Пользователь А" живет в Германии, но работает для компании, которая занимается бизнесом с другими компаниями во Франции. Когда "Пользователь А" хочет создать счет-фактуру (PDF) для одной из этих французских компаний, мы хотим, чтобы этот код генерации счета работал с культурой "fr-FR", а не с культурой de-DE.

Я рассмотрел пару способов сделать это легко, и мне интересно, правильно ли я это сделаю. Мои основные проблемы касаются производительности и безопасности потоков.

Один подход включает статический метод, предназначенный для выполнения заданной задачи с предоставленной культурой. Что-то вроде этого:

 public static void RunWithCulture(CultureInfo culture, Action task)
    {
        if (culture == null)
            throw new ArgumentNullException("culture");

        var originalCulture = new
                                  {
                                      Culture = Thread.CurrentThread.CurrentCulture,
                                      UICulture = Thread.CurrentThread.CurrentUICulture
                                  };

        try
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
            task();
        }
        finally
        {
            Thread.CurrentThread.CurrentCulture = originalCulture.Culture;
            Thread.CurrentThread.CurrentUICulture = originalCulture.UICulture;
        }
    }

Затем этот метод можно вызвать следующим образом:

var customerCulture = new CultureInfo(currentCustomer.Locale);
CultureRunner.RunWithCulture(customerCulture, () => invoiceService.CreateInvoice(currentCustomer.CustomerId));

Я также рассмотрел создание класса, который реализует IDisposable, который будет отвечать за настройку культуры потока в нем ctor, а затем вернуть исходные культуры обратно в методе Dispose, чтобы вы могли называть его следующим образом:

var customerCulture = new CultureInfo(currentCustomer.Locale);
using(new CultureRunner(currentCustomer.Locale))
{
  invoiceService.CreateInvoice(currentCustomer.CustomerId);
}

Неужели я все это делаю неправильно? Что, если какой-либо из этих подходов предпочтительнее?

4b9b3361

Ответ 1

Мне нравится подход using. Я бы также создал метод расширения, чтобы все читалось лучше:

var customerCulture = new CultureInfo(currentCustomer.Locale);  
using (customerCulture.AsCurrent()) {
  invoiceService.CreateInvoice(currentCustomer.CustomerId);
}

Примерно так:

public static class CultureInfoExtensions {
  public static IDisposable AsCurrent(this CultureInfo culture) {
    return new CultureRunner(culture);
  }
}

CultureRunner пример:

public class CultureRunner : IDisposable
{
    readonly CultureInfo originalCulture;
    readonly CultureInfo originalUICulture;

    public CultureRunner(CultureInfo culture)
    {
        if (culture == null)
            throw new ArgumentNullException(nameof(culture));

        originalCulture = Thread.CurrentThread.CurrentCulture;
        originalUICulture = Thread.CurrentThread.CurrentUICulture;

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

    public void Dispose()
    {
        Thread.CurrentThread.CurrentCulture = originalCulture;
        Thread.CurrentThread.CurrentUICulture = originalUICulture;
    }
}

Или, если это всегда ваш объект клиента, который устанавливает культуру, другой метод расширения повысит абстракцию еще больше:

using (currentCustomer.CultureContext()) {
  invoiceService.CreateInvoice(currentCustomer.CustomerId);
}

Ответ 2

Голосовать за делегатский подход RunWithCulture.

Не стесняйтесь добавлять причины/ссылки на этот wiki-пост.

Ответ 3

Так как вы спрашиваете, является ли временная смена текущей культуры темы хорошей идеей, я могу ответить только: нет. Его можно использовать, если и нет другого способа заставить вещи работать. Это связано с тем, что такое переключение подвержено ошибкам. Хорошо, вы не забудете изменить ситуацию обратно с кодом Jordão (уважение), но... На данный момент у вас есть клиенты, которые хотят создавать французские счета-фактуры. Я предполагаю, что вы хотите использовать французский формат даты, числа и валюты. Это нормально. Но... Что делать, если в будущем какое-то будущее нужно будет распечатать в другом формате, например, в этом немецком? Вы собираетесь создать какую-то уродливую работу?

Я понимаю, что это может быть не под вашим контролем (например, Reporting Software может быть 3 rd сторонним автономным решением, и вы не можете контролировать, как он обрабатывает ToString()), но если он находится внутри ваш контроль, я бы рекомендовал подавать данные в правильном формате в первую очередь. Например, вы можете создать некоторый уровень преобразования данных (DTO) и правильно форматировать данные (через ToString(IFormatProvider)). Я знаю, что это довольно много усилий, но поскольку вы спрашиваете о правильном способе делать что-то...

Если бы мы были в одной организации, и я бы сделал обзор кода I18n, вы могли бы быть уверены, что я бы указал на временную меняющуюся культуру как на дефект. Обычно есть способ избежать этого.