如何在HangFire中控制定期作业的CurrentCulture

5

我在一个带有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>

在我的Startup Configuration方法中,我有以下内容

        CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;
        CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CurrentUICulture;

因为有异步方法,我希望所有启动的线程都能共享指定在web.config中的相同的文化。我还有一些在HangFire中运行的定期作业。我的问题是,无论我做什么,这些重复的作业都只会使用服务器文化(da-DK)作为当前文化(当前UI文化似乎会根据我的操作而变化: HangFireDashboard。我已经搜索了Hangfire文档和SO,但没有找到能解决这个问题的东西。我宁愿避免在每个用于重复作业的方法顶部硬编码一个文化。希望有人能帮助我...。
跟进(2017-11-21):
事实证明这与HangFire无关。显然,在运行"Startup.Configuration"时,CultureInfo.CurrentCulture没有设置为web.config中提供的值,因此CultureInfo.DefaultThreadCurrentCulture在此处设置为错误的值。一旦控制器执行,这些值就会被设置为web.config中的任何值。目前,我已决定在Startup类中硬编码文化,以确保在执行任何Hangfire重复作业之前已将其设置为DefaultThreadCurrentCulture。

假设您正在使用 SqlServer,对于作业(如果您正在使用 redis,则直接在作业条目上)在 HangFire.JobParameter 表中显示的 CurrentCulture 和 CurrentUICulture 是什么? - jbl
@jbl:我相信你所指的值与在HangFire仪表板中查看作业时显示的值相同。无论如何-我看到所有这些作业都在CurrentUICulture中具有en-US(我认为它们应该如此),而大多数作业在CurrentCulture中具有da-DK(它们不应该如此)。然而,有一些作业实际上确实将en-US作为CurrentCulture。似乎没有规律可以确定何时工作线程将具有en-US作为CurrentCulture。 - Zrethreal
这可能来自将作业排队的客户端。无论如何,首先尝试从GlobalFilters中删除CaptureCultureAttribute: https://github.com/HangfireIO/Hangfire/blob/129707d66fde24dc6379fb9d6b15fa0b8ca48605/src/Hangfire.Core/GlobalJobFilters.cs#L34,然后提供您自己的代码,硬编码en-us值:https://github.com/HangfireIO/Hangfire/blob/129707d66fde24dc6379fb9d6b15fa0b8ca48605/src/Hangfire.Core/CaptureCultureAttribute.cs - jbl
2个回答

2
我们也遇到了这个问题,在Azure上运行时,RecurringJob的文化是en-US。 这导致DateTime解析问题(以及其他问题),因为我们期望本地文化,因为我们设置了<globalization culture="en-NZ" uiCulture="en-NZ" /> Sample job showing culture 如果手动触发RecurringJob(从HangFire Web界面),或从控制器触发(BackgroundJob.Enqueue),则正确捕获文化。
为了解决这个问题,类似于上面的跟进(2017-11-21),在调用RecurringJob.AddOrUpdate之前,我们强制将文化设置为web.config中定义的文化属性。
我们还发现,仍然使用错误的文化,所以我们不得不用一个调用ForceCulture的CaptureCultureAttribute来替换它。
   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
            //https://dev59.com/46bja4cB1Zd3GeqPnumb
            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
            }
        }

希望这能帮助未来其他人!

1
通过运行GlobalJobFilters.Filters.Remove<CaptureCultureAttribute>();来简单地移除默认的CaptureCultureAttribute - undefined

0

根据源代码,删除操作有点棘手,所以实际上需要删除的是下面的实例: https://github.com/HangfireIO/Hangfire/blob/1ae167e3ffc87429a64cf95201c40508101cf25c/src/Hangfire.Core/Common/JobFilterCollection.cs#L96

var filter = GlobalJobFilters.Filters.OfType<JobFilter>().Where(c => c.Instance is CaptureCultureAttribute).FirstOrDefault().Instance;
            GlobalJobFilters.Filters.Remove(filter);


这将完全移除捕获文化,因为在我看来,它本来就不应该存在。使用实际配置而不是改变当前文化的代码似乎很奇怪。

所以我的启动基本上是:

        static Startup()
        {
            
            var cultureInfo = CultureInfo.GetCultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
  
      
            GlobalJobFilters.Filters.Remove(
                 GlobalJobFilters.Filters.OfType<JobFilter>().Where(c => c.Instance is CaptureCultureAttribute).FirstOrDefault().Instance
            );
          
      
        }

这使得应用程序在所有情况下都使用编码文化运行,如果需要,您可以实现逻辑以从配置文件中读取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接