I have my DbContext (IApplicationDbContext) and another Service (Does API calls) available via Dependency Injection.
I need to run a Hangfire Recurrent Job every 15 minutes to contact several APIs and get the data and then put it into the database. I want to have one class that contains the method for the job, but the job needs access to the DbContext and the Service.
My Service is registered as a Singleton and my DbContext as a Scoped Service.
Can someone please tell me how do I create a class that contains one method that is run by Hangfire every 15 minutes and how do I launch this job?
I tried to create a class called JobContext with an Interface IJobContext and inject the DbContext and the ApiService via the constructor in the JobContext and then register this via AddSingleton, but it did not work, because the lifetime of is shorter as the DbContext (Scoped).
What I need:
Something like this:
public class JobContext : IJobContext
{
public IApplicationDbContext ApplicationDbContext { get; set; }
public IApiService ApiService { get; set; }
public JobContext(IApplicationDbContext applicationDbContext, IApiService apiService)
{
ApplicationDbContext = applicationDbContext;
ApiService = apiService;
InitJobs();
}
public void InitJobs()
{
RecurringJob.AddOrUpdate(() => Execute(), Cron.Minutely);
}
public void Execute()
{
// This is my job... Do some Api requests and save to the Db
Console.WriteLine("123");
}
}
And then what I tried is in the Startup.cs#ConfigureServices (but this fails):
services.AddSingleton<IJobContext, JobContext>();
This is the Exception I get:
System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: ##.##.##.IJobContext Lifetime: Singleton ImplementationType: ##.##.##.JobContext': Cannot consume scoped service '##.##.##.IApplicationDbContext' from singleton'##.##.##.IJobContext'.)
Thank you so much for your help!
You need to create a new instance of your DbContext
in your class. You don't want your DbContext
to be a singleton.
Just inject a scoped service factory into your class. You can use this method to create a new scope and instantiate your scoped services.
public class JobContext : IJobContext
{
public IServiceScopeFactory ServiceScopeFactory { get; set; }
public IApiService ApiService { get; set; }
public JobContext(IServiceScopeFactory serviceScopeFactory, IApiService apiService)
{
ServiceScopeFactory = serviceScopeFactory;
ApiService = apiService;
InitJobs();
}
public void InitJobs()
{
RecurringJob.AddOrUpdate(() => Execute(), Cron.Minutely);
}
public void Execute()
{
using var scope = ServiceScopeFactory.CreateScope();
using var dbContext = scope.ServiceProvider.GetService<IApplicationDbContext>();
// use your dbContext
// This is my job... Do some Api requests and save to the Db
Console.WriteLine("123");
}
}