So, Hangfire provides IRecurringJobManager
we can use to add recurring jobs but it only supports injecting one service and I couldn't find any example of how to do it when I need to access multiple services.
Consider I have two registered services like this:
services.AddTransient<Service1>();
services.AddTransient<Service2>();
And I would like to use them both in my recurring job. Ideally what I would like to do is something like:
recurringJobManager.AddOrUpdate<Service1, Service2>("my-job", (service1, service2) =>
{
// do something
}, Cron.Daily);
But it's not possible.
What is the correct way to do this? Should I inject IServiceProvider? Like this?
recurringJobManager.AddOrUpdate<IServiceProvider>("my-job", (serviceProvider) =>
{
var service1 = serviceProvider.GetRequiredService<Service1>();
var service2 = serviceProvider.GetRequiredService<Service2>();
// do something
}, Cron.Daily);
Will that work? EDIT: no, because I cannot use statement bodies inside
I'm kinda confused because documentation doesn't cover these cases.
One solution seems to be to create a separate class with services I need
public class MyJob
{
private readonly Service1 _service1;
private readonly Service2 _service2;
public MyJob(Service1 service1, Service2 service2)
{
_service1 = service1;
_service2 = service2;
}
public void DoSomething() {}
}
And then use it like:
recurringJobManager.AddOrUpdate<MyJob>("my-job", myJob => myJob.DoSomething(), Cron.Daily);
Note: we cannot use statement body {}
with AddOrUpdate
. It always needs to be a single method call as it takes Expression<T>
as a parameter. So if we try to do recurringJobManager.AddOrUpdate<MyJob>("my-job", myJob => {myJob.DoSomething(); myJob.DoSomething2();}, Cron.Daily);
we will get compiler error.
Another solution is a separate method that takes IServiceProvider as a parameter. But I can't guarantee it works.
// 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);
Also check https://docs.hangfire.io/en/latest/best-practices.html#best-practices
So ideally you shouldn't use IServiceProvider
as job argument because it will be serialized.
⚠️ Important thing to notice that got me extremely confused at first!
when you do
var myJob = new MyJob();
RecurringJob.AddOrUpdate("my-job", () => myJob.Run(), Cron.Hourly(0));
It won't use that instance you created but create a new one every time. So the above code is 100% equivalent with
RecurringJob.AddOrUpdate<MyJob>("my-job", (myJob) => myJob.Run(), Cron.Hourly(0));
Because what AddOrUpdate does internally is it parses expression, determines type of myJob and the method called and constructs a new expression to create instance of that type and call that method on a new instance.