问题
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)?
回答1:
For resoving scoped service from IServiceProvider
, try
using (var scope = _serviceProvider.CreateScope())
{
var job = (IJob)scope.ServiceProvider.GetService(jobDetail.JobType);
}
来源:https://stackoverflow.com/questions/55170110/how-to-integrate-quartz-net-jobs-that-require-scoped-services-injected-in-them