【发布时间】:2019-03-14 18:56:43
【问题描述】:
我正在尝试在 my own answer 之后从 this question 创建一些 Quartz.Net 工作。但是,如果作业相当复杂并且需要“作用域”(services.AddScoped<.., ...>) 服务,则该示例不起作用,因为作业是作为单例创建的。
如果我将它们更改为作用域,则 serviceProvider 不包含我需要的服务。我已经设法使用以下代码使其工作:
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
作业工厂不使用注入的服务提供者,而是在 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) { }
}
我想知道这是否是在 ASP.NET Core 2.0 中处理 Quartz 作业的好方法,因为它看起来更像是一种 hack,而不是真正的解决方案。
问题:如何集成需要在其中注入“范围”服务的 Quartz.Net 作业(ASP.NET Core 2.0)?
【问题讨论】:
-
对于每个请求对应的
Scoped服务,不应该定义为静态属性。尝试将IServiceProvider注入QuartzJobFactory(IServiceProvider serviceProvider)。 -
@TaoZhou - 我的初始代码注入 IServiceProvider,但由于作业被定义为单例(这可能是有道理的,因为我通常想要一个运行的实例)和其他服务为“范围”(我需要这个,因为它们为应用程序的其他部分提供业务逻辑并且必须限定范围)。如果我将作业设为范围,则注入的提供者无法为我提供作业的实例。我编写的代码似乎可以正常工作,我只是不喜欢这种模式(似乎不对)。
标签: asp.net-core asp.net-core-2.0 quartz.net