这是暂时改变当前线程文化的好方法吗?

时间:2022-06-21 22:42:23

I work on a fairly large ASP .NET Web Forms application that is currently used primarily in the United States. We are in the process of rolling it out to other parts of the world, which of course means we are currently working on localizing all areas of the application. Generally speaking our approach has been to set the current thread's CurrentCulture and CurrentUICulture properties at the beginning of each request to support the proper formatting and resource extraction based on the current user's locale.

我在一个相当大的ASP .NET Web Forms应用程序上工作,该应用程序目前主要在美国使用。我们正在将其推广到世界其他地区,这当然意味着我们正在努力本地化应用程序的所有领域。一般来说,我们的方法是在每个请求开始时设置当前线程的CurrentCulture和CurrentUICulture属性,以支持基于当前用户的语言环境的正确格式化和资源提取。

In some cases, however, we have a need to run a certain bit of a code using a culture other than the culture of the current user. For example, 'User A' lives in Germany but works for a company that does business with other companies in France. When 'User A' wants to create an invoice (PDF) for one of those French companies, we want that invoice generation code to run with the 'fr-FR' culture rather than the 'de-DE' culture.

但是,在某些情况下,我们需要使用除当前用户文化之外的文化来运行某些代码。例如,“用户A”居住在德国,但适用于与法国其他公司有业务往来的公司。当“用户A”想要为其中一家法国公司创建发票(PDF)时,我们希望发票生成代码以“fr-FR”文化而非“de-DE”文化运行。

I've considered a couple ways of doing this easily and am wondering if I'm going about this correctly. My main concerns are around performance and thread safety.

我已经考虑过几种方法可以很容易地做到这一点,我想知道我是否正确地做到了这一点。我主要关心的是性能和线程安全性。

One approach involves a static method designed to run a given task with a provided culture. Something like this:

一种方法涉及一种静态方法,旨在使用提供的文化来运行给定任务。像这样的东西:

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

This method could then be invoked like this:

然后可以像这样调用此方法:

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

I've also considered creating a class that implements IDisposable that would be responsible for setting the thread culture in it's ctor and then returning the original cultures back in the Dispose method, so you could call it like this:

我还考虑创建一个实现IDisposable的类,它负责在它的ctor中设置线程文化,然后在Dispose方法中返回原始文化,所以你可以像这样调用它:

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

Am I going about this all wrong? Which, if any of these approaches is preferable?

我错了吗?哪个,如果这些方法中的任何一个更可取?

3 个解决方案

#1


14  

I like the using approach. I'd also create an extension method to make things read better:

我喜欢使用方法。我还创建了一个扩展方法,以便更好地阅读:

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

Something like this:

像这样的东西:

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

Or, if it's always your customer object who sets the culture, another extension method would raise the abstraction even further:

或者,如果始终是您的客户对象设置文化,另一种扩展方法将进一步提升抽象:

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

#2


2  

Vote for delegate RunWithCulture approach here.

在这里投票给代表RunWithCulture方法。

Feel free to add reasons/links to this wiki post.

随意添加这个维基帖的原因/链接。

#3


2  

Since you are asking if temporary changing Current Thread's Culture is good idea, I can only answer: no. It could be used if and only there is no other way to get things working. That is just because such switching is error prone. OK, you won't forget to change things back with the code Jordão (respect) gave you, but...
For now you have customers that want to create French invoices. I assuming that you want to use French date, number and currency formats. That's OK. But... What if in the future certain future would need to be printed out with other format, for example this originating German? Are you going to create some kind of ugly work-around?

既然你在问临时改变当前线程的文化是不是一个好主意,我只能回答:不。只有在没有其他办法让事情发挥作用的情况下才可以使用它。这只是因为这种切换容易出错。好的,你不会忘记用Jordão(尊重)给你的代码改回来,但是......现在你有了想要创建法国发票的客户。我假设你想使用法国日期,数字和货币格式。没关系。但是......如果将来某些未来需要以其他格式打印出来,例如这个原始的德国人呢?你打算创造一些丑陋的工作吗?

I understand that it could be beyond your control (like Reporting Software could be 3rd party stand-alone solution and you could not control how it is handling ToString()) but if it is within your control, I would recommend feeding the data in right format in the first place. For example you could create some data transforming layer (DTO) and format data correctly (via ToString(IFormatProvider)). I know this is quite an effort but since you are asking about correct way to do things...

据我所知,它可能超出您的控制范围(如报告软件可能是第三方独立解决方案,您无法控制它如何处理ToString())但如果它在您的控制范围内,我建议您输入正确的数据格式首先。例如,您可以正确创建一些数据转换层(DTO)和格式数据(通过ToString(IFormatProvider))。我知道这是一个很大的努力但是因为你问的是正确的做事方式......

If we were in the same organization and I would do I18n code review, you could be sure that I would point out temporary changing culture as a defect. Usually there is a way to avoid this.

如果我们在同一个组织中并且我会进行I18n代码审查,那么您可以确定我会指出暂时改变文化作为缺陷。通常有一种方法可以避免这种情况。

#1


14  

I like the using approach. I'd also create an extension method to make things read better:

我喜欢使用方法。我还创建了一个扩展方法,以便更好地阅读:

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

Something like this:

像这样的东西:

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

Or, if it's always your customer object who sets the culture, another extension method would raise the abstraction even further:

或者,如果始终是您的客户对象设置文化,另一种扩展方法将进一步提升抽象:

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

#2


2  

Vote for delegate RunWithCulture approach here.

在这里投票给代表RunWithCulture方法。

Feel free to add reasons/links to this wiki post.

随意添加这个维基帖的原因/链接。

#3


2  

Since you are asking if temporary changing Current Thread's Culture is good idea, I can only answer: no. It could be used if and only there is no other way to get things working. That is just because such switching is error prone. OK, you won't forget to change things back with the code Jordão (respect) gave you, but...
For now you have customers that want to create French invoices. I assuming that you want to use French date, number and currency formats. That's OK. But... What if in the future certain future would need to be printed out with other format, for example this originating German? Are you going to create some kind of ugly work-around?

既然你在问临时改变当前线程的文化是不是一个好主意,我只能回答:不。只有在没有其他办法让事情发挥作用的情况下才可以使用它。这只是因为这种切换容易出错。好的,你不会忘记用Jordão(尊重)给你的代码改回来,但是......现在你有了想要创建法国发票的客户。我假设你想使用法国日期,数字和货币格式。没关系。但是......如果将来某些未来需要以其他格式打印出来,例如这个原始的德国人呢?你打算创造一些丑陋的工作吗?

I understand that it could be beyond your control (like Reporting Software could be 3rd party stand-alone solution and you could not control how it is handling ToString()) but if it is within your control, I would recommend feeding the data in right format in the first place. For example you could create some data transforming layer (DTO) and format data correctly (via ToString(IFormatProvider)). I know this is quite an effort but since you are asking about correct way to do things...

据我所知,它可能超出您的控制范围(如报告软件可能是第三方独立解决方案,您无法控制它如何处理ToString())但如果它在您的控制范围内,我建议您输入正确的数据格式首先。例如,您可以正确创建一些数据转换层(DTO)和格式数据(通过ToString(IFormatProvider))。我知道这是一个很大的努力但是因为你问的是正确的做事方式......

If we were in the same organization and I would do I18n code review, you could be sure that I would point out temporary changing culture as a defect. Usually there is a way to avoid this.

如果我们在同一个组织中并且我会进行I18n代码审查,那么您可以确定我会指出暂时改变文化作为缺陷。通常有一种方法可以避免这种情况。