我根据这个问题的自己的回答,想创建一些Quartz.Net作业。但是,如果作业相当复杂并且需要“作用域”()服务,则该示例将不起作用,因为作业是作为单例创建的。services.AddScoped<.., ...>
如果我将它们更改为范围,则serviceProvider不包含我需要的服务。我设法使用以下代码使其工作:
/// <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));
}
作业工厂不使用注入的服务提供程序,而是在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)?
要从中保存作用域服务IServiceProvider
,请尝试
using (var scope = _serviceProvider.CreateScope())
{
var job = (IJob)scope.ServiceProvider.GetService(jobDetail.JobType);
}
我已经尝试过这一点,并且不得不将添加作业更改为作用域而不是单例(由于依赖作用域的服务)。但是,这样做之后,dbcontext似乎混乱了(试图使用已经处置的上下文)。我如何添加它(
services.AddDbContext<CustomApiContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("Default")));
)并不奇怪@Alexei是什么意思
had to change add jobs as scoped
?为此2: if jobs are scoped, the provider cannot solve the injected services
,它指示您被注入为作用域作业。是否有任何演示可以重现您的问题?我的初始职位注册如下:
services.Add(jobs.Select(jobType => new ServiceDescriptor(jobType, jobType, ServiceLifetime.Singleton)))
。由于MyJob是单例,因此失败并显示诸如“无法将范围内的服务...注入MyJob”之类的错误。这很有意义,所以我已更改为“ services.Add(jobs.Select(jobType => new ServiceDescriptor(jobType,jobType,ServiceLifetime.Scoped)));”;。现在数据库上下文表明我正在尝试访问已处置的上下文,目前尚不清楚下一步该怎么做。您的答案是正确的,因为我终于在这里找到了我的问题。谢谢。
惊人的!这行得通!