I am making a web API using ASP.NET Core and now I am having a problem with quartz scheduled jobs. The jobs I have will access my services to update the database. After some researches, I figured how to do the dependency injection so that my jobs can access the services, here is how I overrode the job factory:
public class AspNetCoreJobFactory : SimpleJobFactory
{
IServiceProvider _provider;
public AspNetCoreJobFactory(IServiceProvider provider)
{
_provider = provider;
}
public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return (IJob)this._provider.GetService(bundle.JobDetail.JobType);
}
catch(Exception e)
{
throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
}
}
}
and I added this line on my startup configure:
_quartzScheduler.JobFactory = new AspNetCoreJobFactory(app.ApplicationServices);
Lastly I added those two lines on my ConfigureServices
method:
services.AddSingleton<IUserService, UserService>();
services.AddTransient<BatchJobCheckContract>();
right now I am getting this exception when trying to execute the job, it seems like it's because my service uses the DbContext
, how can I solve this?
Cannot consume scoped service 'RHP.data.RHPDbContext' from singleton 'RHP.data.IServices.Administration.IUserService'.
After playing around with Quartz (version 3.2.3), it looks like you do not have to write your own JobFactory
to use Microsoft DI. (See ASP.NET Core Integration and Microsoft DI Integration):
Add the Quartz.AspNetCore
nuget package and you can scoped services like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IScopedService, ScopedService>();
// Job has scoped dependencies, so it must be scoped as well
services.AddScoped<Job>();
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
var jobKey = new JobKey("job");
q.AddJob<Job>(jobKey);
q.AddTrigger(t => /* ... */));
});
services.AddQuartzServer(opts => opts.WaitForJobsToComplete = true);
}
However, if you cannot use the current version of Quartz.AspNetCore
, you could still
use IServiceProvider
as dependency in your Job
class and resolve services there:
public class Job : IJob
{
private readonly IServiceProvider _serviceProvider;
public Job(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Task Execute(IJobExecutionContext context)
{
using var scope = _serviceProvider.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
// ...
}
}
Like this the Job
class controls the lifetime of scoped services.
I've previously had a similar problem with background tasks, you might need to create a scope.
I've adapted this code and applied it to your use case.
public class AspNetCoreJobFactory : SimpleJobFactory
{
IServiceProvider _provider;
public AspNetCoreJobFactory(IServiceProvider provider)
{
_provider = provider;
}
public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
using(var serviceScope = _provider.CreateScope())
{
var services = serviceScope.ServiceProvider.
return (IJob)services.GetService(bundle.JobDetail.JobType);
}
}
catch(Exception e)
{
throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With