如何控制 HangFire 中重复作业的 CurrentCulture

Zre*_*eal 5 c# currentculture owin asp.net-web-api2 hangfire

我在 Owin 的 asp.net WebApi2 解决方案中使用 HangFire 1.6。

服务器是针对一种文化(da-DK)设置的 - 我无法更改这一点。我的应用程序必须使用另一种区域性 (en-US) 来正确解析它接收到的文本数据。基本上,我只是希望解决方案中的所有内容都使用其他区域性,并且永远不要使用服务器上的区域性。

在我的 web.config 中我有:

<system.web>
    <httpRuntime targetFramework="4.6.1" />
    <globalization enableClientBasedCulture="false" culture="en-US" uiCulture="en-US"/>
</system.web>
Run Code Online (Sandbox Code Playgroud)

在我的Startup Configuration方法中我有

        CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;
        CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CurrentUICulture;
Run Code Online (Sandbox Code Playgroud)

因为有异步方法,并且我希望所有线程开始共享 web.config 中指定的相同区域性

我还在 HangFire 中运行一些经常性作业

我的问题是,无论我做什么,重复的工作都只是继续使用服务器文化(da-DK)作为 CurrentCulture (CurrentUICulture 似乎对我所做的事情做出反应: HangFire仪表板

我搜索了 Hangfire 文档等,但没有找到任何可以为我解决这个问题的方法。我宁愿避免在重复工作中使用的每个方法的顶部对文化进行硬编码

希望可以有人帮帮我...

后续(2017-11-21):

事实证明,这与 HangFire 无关。显然在运行CultureInfo.CurrentCulture时尚未设置为 web.config 中提供的值,因此此处设置为错误的值。一旦控制器执行,这些值就会设置为 web.config 中的任何内容。Startup.ConfigurationCultureInfo.DefaultThreadCurrentCulture

现在,我决定在 Startup 类中对区域性进行硬编码,以确保在执行任何 Hangfire 重复作业之前已将其设置为 DefaultThreadCurrentCulture

agr*_*ath 3

我们也遇到了这个问题,在 Azure 上运行时,HangFire 工作的文化RecurringJob 是 en-US 文化。这会导致日期时间解析问题(除其他外),因为我们期望我们设置的本地文化<globalization culture="en-NZ" uiCulture="en-NZ" /> 展示文化的示例工作

如果RecurringJob是手动触发(从 HangFire Web 界面)或从控制器 ( BackgroundJob.Enqueue) 触发,则可以正确捕获区域性。

为了解决这个问题,与上面的后续 (2017-11-21)类似,我们在调用 之前将区域性强制为 web.config 中 Globalization 属性中定义的内容RecurringJob.AddOrUpdate

我们还发现 RecurringJob 仍然使用了错误的区域性,因此我们必须首先将 CaptureCultureAttribute 替换为名为 ForceCulture 的区域性。

   public static void ConfigureHangfire(this IAppBuilder app)
        {
            //preamble removed <snip>

            app.UseHangfireServer();

         //force the culture objects to be loaded from system.web/globalization
            ForceCulture();

            //force remove the default CaptureCultureAttribute
Hangfire.GlobalJobFilters.Filters.Remove(Hangfire.GlobalJobFilters.Filters.Where(f => f.Instance is CaptureCultureAttribute).First());

            //add our one that forces the culture

            Hangfire.GlobalJobFilters.Filters.Add(new ForceCultureAttribute());

            //RecurringJob.AddOrUpdate...<snip>
        }

private static void ForceCulture()
        {
            //when the RecurringJob.AddOrUpdate runs, the CultureInfo.DefaultThreadCurrentCulture is not yet set, so RecurringJobs get the wrong culture
            //leading to DateTime parse issues & friends
            ///sf/ask/3317758951/
            GlobalizationSection globalizationConfig = (GlobalizationSection)ConfigurationManager.GetSection("system.web/globalization");
            var culture = new CultureInfo(globalizationConfig.Culture);
            var uiCulture = new CultureInfo(globalizationConfig.UICulture);
            CultureInfo.DefaultThreadCurrentCulture = culture;
            CultureInfo.DefaultThreadCurrentUICulture = uiCulture;
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = uiCulture;
        }

//Taken directly from Hangfire source, with the exception of the call to ForceCulture inside OnCreating
//https://github.com/HangfireIO/Hangfire/blob/129707d66fde24dc6379fb9d6b15fa0b8ca48605/src/Hangfire.Core/CaptureCultureAttribute.cs

public sealed class ForceCultureAttribute : JobFilterAttribute, IClientFilter, IServerFilter
        {
            public void OnCreating(CreatingContext filterContext)
            {
                ForceCulture();

                if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));

                filterContext.SetJobParameter(
                    "CurrentCulture", CultureInfo.CurrentCulture.Name);
                filterContext.SetJobParameter(
                    "CurrentUICulture", CultureInfo.CurrentUICulture.Name);
            }

            public void OnCreated(CreatedContext filterContext)
            {
            }

            public void OnPerforming(PerformingContext filterContext)
            {
                var cultureName = filterContext.GetJobParameter<string>("CurrentCulture");
                var uiCultureName = filterContext.GetJobParameter<string>("CurrentUICulture");

                if (!String.IsNullOrEmpty(cultureName))
                {
                    filterContext.Items["PreviousCulture"] = CultureInfo.CurrentCulture;
                    SetCurrentCulture(new CultureInfo(cultureName));
                }

                if (!String.IsNullOrEmpty(uiCultureName))
                {
                    filterContext.Items["PreviousUICulture"] = CultureInfo.CurrentUICulture;
                    SetCurrentUICulture(new CultureInfo(uiCultureName));
                }
            }

            public void OnPerformed(PerformedContext filterContext)
            {
                if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));

                if (filterContext.Items.ContainsKey("PreviousCulture"))
                {
                    SetCurrentCulture((CultureInfo)filterContext.Items["PreviousCulture"]);
                }
                if (filterContext.Items.ContainsKey("PreviousUICulture"))
                {
                    SetCurrentUICulture((CultureInfo)filterContext.Items["PreviousUICulture"]);
                }
            }

            private static void SetCurrentCulture(CultureInfo value)
            {
#if NETFULL
            System.Threading.Thread.CurrentThread.CurrentCulture = value;
#else
                CultureInfo.CurrentCulture = value;
#endif
            }

            // ReSharper disable once InconsistentNaming
            private static void SetCurrentUICulture(CultureInfo value)
            {
#if NETFULL
            System.Threading.Thread.CurrentThread.CurrentUICulture = value;
#else
                CultureInfo.CurrentUICulture = value;
#endif
            }
        }
Run Code Online (Sandbox Code Playgroud)

希望这对将来的其他人有帮助!