【问题标题】:Add recurring job that uses multiple services添加使用多个服务的重复作业
【发布时间】:2021-03-26 09:33:40
【问题描述】:

因此,Hangfire 提供了IRecurringJobManager,我们可以使用它来添加重复作业,但它只支持注入一项服务,当我需要访问多项服务时,我找不到任何示例。

假设我有两个这样的注册服务:

services.AddTransient<Service1>();
services.AddTransient<Service2>();

我想在我的经常性工作中同时使用它们。 理想情况下,我想做的是:

recurringJobManager.AddOrUpdate<Service1, Service2>("my-job", (service1, service2) =>
{
    // do something
}, Cron.Daily);

但这是不可能的。

这样做的正确方法是什么?我应该注入 IServiceProvider 吗?像这样?

recurringJobManager.AddOrUpdate<IServiceProvider>("my-job", (serviceProvider) =>
{
    var service1 = serviceProvider.GetRequiredService<Service1>();
    var service2 = serviceProvider.GetRequiredService<Service2>();
    // do something
}, Cron.Daily);

这行得通吗? 编辑:不,因为我不能在里面使用语句体

我有点困惑,因为文档没有涵盖这些情况。

【问题讨论】:

标签: c# asp.net-core hangfire


【解决方案1】:

不确定这是否符合您的需求,但如果两个服务具有您正在调用的相同方法(名称),请让它们都实现相同的接口,以便您可以将它们注入构造函数。注意:您必须使用它们的接口注册服务。在构造函数中使用 IEnumerable 进行注入应该会给你一个实现该接口的所有服务的实例:

public interface ICronJob
{
    void Run();
}

class Service1 : ICronJob
...

class Service2 : ICronJob
...

public class MyJob
{
    private readonly IEnumerable<ICronJob> _services;

    public MyJob(IEnumerable<ICronJob> services)
    {
        _services = services;
    }

    public void DoSomething() 
    {
        foreach (ICronJob service in services)
            service.Run();
    }

}

【讨论】:

    【解决方案2】:

    一个解决方案似乎是用我需要的服务创建一个单独的类

    public class MyJob
    {
         private readonly Service1 _service1;
         private readonly Service2 _service2;
    
         public MyJob(Service1 service1, Service2 service2)
         {
             _service1 = service1;
             _service2 = service2;
         }
    
         public void DoSomething() {}
    }
    

    然后像这样使用它:

    recurringJobManager.AddOrUpdate<MyJob>("my-job", myJob => myJob.DoSomething(), Cron.Daily);
    

    注意:我们不能将声明正文 {}AddOrUpdate 一起使用。它总是需要是一个单一的方法调用,因为它需要Expression&lt;T&gt; 作为参数。所以如果我们尝试做recurringJobManager.AddOrUpdate&lt;MyJob&gt;("my-job", myJob =&gt; {myJob.DoSomething(); myJob.DoSomething2();}, Cron.Daily);,我们会得到编译器错误。

    另一种解决方案是使用 IServiceProvider 作为参数的单独方法。但我不能保证它有效。

    // Or just use MyJob without any parameters if it has access to Service1 and Service2 in case they're members of your class.
    private void MyJob(IServiceProvider services)
    {
        var service1 = services.GetRequiredService<Service1>();
        var service2 = services.GetRequiredService<Service2>();
    
        // do something
    }
    
    // Add like this or just inject `IServiceProvider` somewhere else e.g. in Configure method
    recurringJobManager.AddOrUpdate<IServiceProvider>("my-job", services => MyJob(services), Cron.Daily);
    

    同时检查https://docs.hangfire.io/en/latest/best-practices.html#best-practices

    因此,理想情况下,您不应使用 IServiceProvider 作为作业参数,因为它将被序列化。

    ⚠️ 重要的事情一开始让我非常困惑!

    当你这样做时

    var myJob = new MyJob();
    RecurringJob.AddOrUpdate("my-job", () => myJob.Run(), Cron.Hourly(0));
    

    它不会使用您创建的实例,而是每次都创建一个新实例。所以上面的代码100%等价于

    RecurringJob.AddOrUpdate<MyJob>("my-job", (myJob) => myJob.Run(), Cron.Hourly(0));
    

    因为 AddOrUpdate 在内部所做的是解析表达式,确定 myJob 的类型和调用的方法,并构造一个新表达式来创建该类型的实例并在新实例上调用该方法。

    【讨论】:

      猜你喜欢
      • 2012-06-02
      • 2022-11-15
      • 2019-01-08
      • 2012-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多