I am trying to create some Quartz.Net jobs following my own answer from this question. However, if the job is fairly complex and required "scoped" (services.AddScoped<.., ...>
) services, the example does not work because jobs are created as singletons.
If I change them to be scoped, the serviceProvider does not contain the services I need. I have managed to make it work using the following code:
Startup.cs
/// <summary>
/// service provider to be used by qiaryz job factory which cannot use its default provider
/// since child services are scoped and the jobs are singleton
/// </summary>
public static IServiceProvider QuartzScopedProvider { get; private set; }
private void ConfigureQuartz(IServiceCollection services, params Type[] jobs)
{
services.AddSingleton<IJobFactory, QuartzJobFactory>();
services.Add(jobs.Select(jobType => new ServiceDescriptor(jobType, jobType, ServiceLifetime.Singleton)));
QuartzScopedProvider = services.BuildServiceProvider();
services.AddSingleton(provider =>
{
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler().Result;
scheduler.JobFactory = provider.GetService<IJobFactory>();
scheduler.Start();
return scheduler;
});
}
/// <summary>
/// configures quartz services
/// </summary>
/// <param name="services"></param>
protected virtual void ConfigureJobsIoc(IServiceCollection services)
{
// all custom services are already defined at this point
ConfigureQuartz(services, typeof(ComplexJob));
}
/// <summary>
/// configures and starts async jobs (Quartz)
/// </summary>
/// <param name="app"></param>
/// <param name="lifetime"></param>
protected virtual void StartJobs(IApplicationBuilder app, IApplicationLifetime lifetime)
{
var scheduler = app.ApplicationServices.GetService<IScheduler>();
QuartzServicesUtilities.StartJob<ComplexJob>(scheduler, TimeSpan.FromMinutes(60));
lifetime.ApplicationStarted.Register(() => scheduler.Start());
lifetime.ApplicationStopping.Register(() => scheduler.Shutdown(waitForJobsToComplete: true));
}
QuartzJobFactory.cs
The job factory does not use the injected service provider, but the one explicitly constructed in Startup.cs
public class QuartzJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
/// <inheritdoc/>
public QuartzJobFactory()
{
// _serviceProvider = serviceProvider;
_serviceProvider = Startup.QuartzScopedProvider;
}
/// <inheritdoc/>
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var jobDetail = bundle.JobDetail;
// this fails with injected service provider:
// 1: cannot inject scoped services in singleton service
// 2: if jobs are scoped, the provider cannot solve the injected services
var job = (IJob)_serviceProvider.GetService(jobDetail.JobType);
return job;
}
/// <inheritdoc/>
public void ReturnJob(IJob job) { }
}
I am wondering if this is a good way to deal with Quartz jobs in ASP.NET Core 2.0 because it looks more like a hack than a real solution.
Question: How to integrate Quartz.Net jobs that require "scoped" services injected in them (ASP.NET Core 2.0)?
Scoped
service which is corresponding to every request, you should not define as static property. Try to injectIServiceProvider
toQuartzJobFactory(IServiceProvider serviceProvider)
. – Buncombe