我该如何在 .net core 中配置 Quartz 以使用依赖注入?我正在使用标准的 .net core 依赖机制。在实现 IJob 接口的类的构造函数中,我需要注入一些依赖项。
Quartz.Spi.IJobFactory
接口并实现它。Quartz文档说明如下:ISchedulerFactory schedulerFactory = new StdSchedulerFactory(properties);
var scheduler = schedulerFactory.GetScheduler();
scheduler.JobFactory = jobFactory;
编辑
实现可以如下所示:
public class JobFactory : IJobFactory
{
protected readonly IServiceProvider Container;
public JobFactory(IServiceProvider container)
{
Container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return Container.GetService(bundle.JobDetail.JobType) as IJob;
}
public void ReturnJob(IJob job)
{
// i couldn't find a way to release services with your preferred DI,
// its up to you to google such things
}
}
Microsoft.Extensions.DependencyInjection
一起使用,请按以下方式创建容器:var services = new ServiceCollection();
services.AddTransient<IAuthorizable, AuthorizeService>();
var container = services.BuildServiceProvider();
var jobFactory = new JobFactory(container);
参考资料
TypeFactory
是什么类型? - donex93受到Rabbans优秀回答的启发,我创建了一个完整的Microsoft.Extensions.DependencyInjection
作业工厂实现:
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Spi;
using System;
using System.Collections.Concurrent;
class JobFactory : IJobFactory
{
protected readonly IServiceProvider _serviceProvider;
protected readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new ConcurrentDictionary<IJob, IServiceScope>();
public JobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var scope = _serviceProvider.CreateScope();
IJob job;
try
{
job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
catch
{
// Failed to create the job -> ensure scope gets disposed
scope.Dispose();
throw;
}
// Add scope to dictionary so we can dispose it once the job finishes
if (!_scopes.TryAdd(job, scope))
{
// Failed to track DI scope -> ensure scope gets disposed
scope.Dispose();
throw new Exception("Failed to track DI scope");
}
return job;
}
public void ReturnJob(IJob job)
{
if (_scopes.TryRemove(job, out var scope))
{
// The Dispose() method ends the scope lifetime.
// Once Dispose is called, any scoped services that have been resolved from ServiceProvider will be disposed.
scope.Dispose();
}
}
}
// Prepare the DI container
var services = new ServiceCollection();
// Register job
services.AddTransient<MyJob>();
// Register job dependencies
services.AddTransient<IFoo, Foo>();
var container = services.BuildServiceProvider();
// Create an instance of the job factory
var jobFactory = new JobFactory(container);
// Create a Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory(properties);
var scheduler = schedulerFactory.GetScheduler();
// Tell the scheduler to use the custom job factory
scheduler.JobFactory = jobFactory;
这个实现已经在一个.NET Core 2.1控制台应用程序中进行了测试,只有一个作业,运行良好。 欢迎留下您的反馈或改进建议...
var jobFactory = new JobFactory(serviceProvider);
然后在 IJob
的构造函数中调用注入,但是出现了 Quartz.SchedulerException
。 - Tomasz KowalczykNo service for type 'xxx.yyy.TestJob' has been registered.
请问您有可用的工作注册示例吗? - Seany84services.AddTransient<YourJob>();
。我已经更新了答案并提供了一个示例。 - CodeZombieservices.AddSingleton<ITypeLoadHelper, SimpleTypeLoadHelper>();
对我来说,总体解决方案如下:
services.AddTransient<UpcomingReleasesNotificationJob>();
services.AddSingleton<ITypeLoadHelper, SimpleTypeLoadHelper>();
var jobKey = new JobKey("notificationJob");
services.AddQuartz(q =>
{
q.SchedulerId = "JobScheduler";
q.SchedulerName = "Job Scheduler";
q.UseMicrosoftDependencyInjectionScopedJobFactory();
q.AddJob<UpcomingReleasesNotificationJob>(j => j.WithIdentity(jobKey));
q.AddTrigger(t => t
.WithIdentity("notificationJobTrigger")
.ForJob(jobKey)
.StartNow()
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(14, 00))
);
});
services.AddQuartzServer(options =>
{
options.WaitForJobsToComplete = true;
});
services.AddQuartz(
q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.ScheduleJob<JobWithDI>(trigger => {});
}
services.AddQuartzHostedService();
services.AddQuartz(s => s.FromAssemblyOf<Program>())
然后,你可以使用 provider.StartQuartz() 来启动 Scheduler(如果可用,它会自动查找 IApplicationLifetime 并为 Shutdown 注册 Scheduler),或者使用传统的 DI 获取和启动服务(provider.GetService().Start();)。
希望这能帮到你!
IJobFactory
的实现方式,并且它起作用了。在使用IServiceScopeFactory
而不是IServiceProvider
的情况下,它与众不同-这确保每次创建新的范围。public class CommonJobFactory : IJobFactory
{
private readonly ILogger<CommonJobFactory> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new ConcurrentDictionary<IJob, IServiceScope>();
public CommonJobFactory(
IServiceScopeFactory scopeFactory,
ILogger<CommonJobFactory> logger)
{
_serviceScopeFactory = scopeFactory;
_logger = logger;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var newScope = _serviceScopeFactory.CreateScope();
try
{
if (newScope.ServiceProvider.GetService(bundle.JobDetail.JobType) is IJob job && _scopes.TryAdd(job, newScope))
return job;
throw new NullReferenceException("Unable to create new job from scope");
}
catch (Exception ex)
{
newScope.Dispose();
_logger.LogError(ex, "Error while constructing {jobType} from scope", bundle.JobDetail.JobType);
throw;
}
}
public void ReturnJob(IJob job)
{
var disposableJob = job as IDisposable;
disposableJob?.Dispose();
if (_scopes.TryRemove(job, out var scope))
scope.Dispose();
}
}