2

Hangfire看起来很光滑。Hangfire但是,我在激活正确的具体类时遇到了挑战。

一些背景:

public interface IJob
{
    bool Execute(string payload);
}
public interface IJobPayload
{
    string UserId { get; set; }
    string JobName { get; set; }
    string JobQueueName { get; set; }
    int Id { get; set; }
    JobType JobType { get; set; }
    CronExpression Cron { get; set; }
}

现在,我(可能)拥有数百个作业,它们继承自IJob并在继承自IJobPayload. 在没有深入了解每个作业的执行代码的情况下,我有类似的东西:

[Queue("critical")] class Job1 : IJob {...}
[Queue("doors")] class Job2 : IJob {...}
[Queue("doors")] class Job3 : IJob {...}
[Queue("lights")] class Job4 : IJob {...}
[Queue("lights")] class Job5 : IJob {...}
[Queue("adhoc")] class Job6 : IJob {...}
...
[Queue("critical")] class JobN : IJob {...}

要提供基本作业的样本:

public class JobDoorStatusChanged : IJob
{
    [Queue("doors")]
    public bool Execute(string payload)
    {
        var command = JsonConvert.DeserializeObject<Payload>(payload);
        // handle execution here...
        return true/false;
    }

    public class Payload : IJobPayload
    {
        public string UserId { get; set; }
        public string JobName { get; set; }
        public string JobQueueName { get; set; }
        public int Id { get; set; }
        public JobType JobType { get; set; }
        public CronExpression Cron { get; set; }
    }
}

我有一个非常简单的 Web API Post 控制器:

[HttpPost]
public string Post()
{
    var payload = Request.Content.ReadAsStringAsync().Result;
    var queueHandler = new QueueHandler();
    return queueHandler.Load(payload);
}

下一步是我遇到失败的地方。他和我正在成为最好的朋友。很遗憾!

Hangfire 有 4 个 Enqueue 方法(2 个同步,2 个异步):

public static string Enqueue([NotNull, InstantHandle] Expression<Action> methodCall)
public static string Enqueue([NotNull, InstantHandle] Expression<Func<Task>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Action<T>> methodCall)
public static string Enqueue<T>([NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)

他们要么采用静态类:

var id = BackgroundJob.Enqueue(() => MyStaticJob.Execute(payload));

或者他们采用应该可以从 Unity 解析的类型:

var id = BackgroundJob.Enqueue<ConcreteJobDefinition>(a => a.Execute(payload));

因为我有几十个,分数甚至太多的对象都是基于 IJob的,这些入口点都不适合我。

我什至尝试了一个类来包装我所有的工作并让 Hangfire 执行单个类,到目前为止,没有运气:

public interface IJobService
{
    bool Execute(string payload);
}
public class JobService : IJobService
{
    private readonly IUnityContainer _container = UnityConfig.GetConfiguredContainer();
    public bool Execute(string payload)
    {
        var command = JsonConvert.DeserializeObject<PayloadStub>(payload);
        var job = _container.Resolve<IJob>(command.JobName);
        return job.Execute(payload);
    }
    internal class PayloadStub : IJobPayload
    {
        public string UserId { get; set; }
        public string JobName { get; set; }
        public string JobQueueName { get; set; }
        public int Id { get; set; }
        public JobType JobType { get; set; }
        public CronExpression Cron { get; set; }
    }
}

现在我可以通过这种方式执行一个具体的实现(谈论混淆!):

var id = BackgroundJob.Enqueue<JobService>(a => a.Execute(payload));

依然没有! 而且,你失去了Queue Attribute每一份工作!

所以,我回到我的startup.cs文件:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        Framework.Initialize(); // internal framework here at work...
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        UnityConfig.RegisterUnity();           // <== UNITY ==
        ConfigureAuth(app);
        HangfireConfig.RegisterHangfire(app);  // <== HANGFIRE ==
    }
}

类 UnityConfig:

public class UnityConfig
{
    public static void RegisterUnity()
    {
        var container = Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer();

        // Register Auth & Exception handlers...
        container.RegisterType<IAccessDeniedResult, AccessDeniedResult>();
        container.RegisterType<IExceptionResult, ExceptionResult>();
        container.RegisterType<IJobService, JobService>();

        // Register all IJob concrete implementations found in this project...
        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(type => typeof(IJob).IsAssignableFrom(type) && type.IsClass),
            WithMappings.FromAllInterfaces,
            t => t.IsNested ? t.DeclaringType.Name + "." + t.Name : t.Name,
            WithLifetime.Transient);
    }
}

类 HangfireConfig:

public class HangfireConfig
{
    public static void RegisterHangfire(IAppBuilder app)
    {
        GlobalConfiguration.Configuration.UseSqlServerStorage(Globals.DatabaseHangfire);
        GlobalJobFilters.Filters.Add(new LogAttribute());
        app.UseHangfireDashboard();
        var options = new BackgroundJobServerOptions
        {
            Queues = Globals.QueueNames,
            Activator = new UnityJobActivator(Sol3.Web.WebApi.App_Start.UnityConfig.GetConfiguredContainer()),
        };
        app.UseHangfireServer(options);

    }
}

我看不出有什么问题。Unity 有所有的注册。由于我有这么多基于单个接口的对象,我知道我打破了 Unity 通常用于依赖注入的方式。

有什么想法吗?您可以看到对代码的任何调整吗?

TIA

4

3 回答 3

1

我遇到了类似的问题,并使用了一个通用的包装器来解决它,该包装器JobHandler<T>将工作排入队列。它是这样工作的:

首先,一个基础JobHandler

public abstract class JobHandler
{
    public abstract void Enqueue(string payload);
}

接下来,JobHandler<T>它将使用 Hangfire 作为正确的具体类型处理排队:

public abstract class JobHandler<T> : JobHandler 
    where T: IJob
{
    public override void Enqueue(string payload)
    {
        BackgroundJob.Enqueue<T>(x => x.Execute(payload));
    }
}

JobHandler<T>不一定需要是抽象的,但在我的情况下,我有处理程序执行的附加逻辑,并为每种作业类型提供它的具体实现。然后将每种类型的处理程序注册到我的应用程序中,我可以简单地执行以下操作:

JobHandler handler = Activator.CreateInstance(GetHandler("MyJobType")) as JobHandler;

handler.Enqueue(payload)

应该很容易使用 Unity 来解决这个问题JobHandler。也就是说,我不确定这是否比简单地解决IJob和排队实例有多大好处,因为导致您的问题的错误已得到修复。

于 2016-11-09T21:40:22.180 回答
1

看起来开发人员发现了一个错误并已修复它。我现在正在对其进行测试,到目前为止,它看起来比没有更合适。

吊火问题 #656

我还发现我的定义有一个错误。我需要将我的Executebool更改为void返回类型。

于 2016-08-31T22:47:42.790 回答
0

虽然核心概念略有不同,并且可能有一种更简洁的方法,但您可以尝试将 Jobs 注册为 IEnumerable,然后根据名称选择您需要的。我有一个相关的问题,我想根据传入的标准为实现处理 N 个处理程序。我在我的博客中写了一篇关于它的文章。

这个 Stack Overflow 答案中有一个 Unity 示例

于 2016-08-30T23:43:10.167 回答