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

发布于 2024-11-03 13:56:43 字数 1820 浏览 5 评论 0原文

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

然而,在某些情况下,我们需要使用当前用户的文化之外的文化来运行某些代码。例如,“用户 A”居住在德国,但在一家与法国其他公司有业务往来的公司工作。当“用户 A”想要为其中一家法国公司创建发票 (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 的类,该类负责在其构造函数中设置线程区域性,然后在 Dispose 方法中返回原始区域性,因此您可以这样称呼它:

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

我的做法是错的吗?如果有的话,哪种方法更可取?

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.

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.

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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

血之狂魔 2024-11-10 13:56:43

我喜欢 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);
}

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

CultureRunner example:

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

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);
}
想你的星星会说话 2024-11-10 13:56:43

既然你问暂时改变当前线程的文化是否是个好主意,我只能回答:。当且仅当没有其他方法使事情正常运转时才可以使用它。那只是因为这种切换很容易出错。好吧,你不会忘记用 Jordão(尊重)给你的代码把事情改回来,但是......
目前,您有想要创建法国发票的客户。我假设您想使用法语日期、数字和货币格式。没关系。但是...如果将来某个未来需要以其他格式打印出来怎么办,例如这个源自德语的格式?你打算创造某种丑陋的解决方法吗?

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

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

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?

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...

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文