Как настроить кварц в ядре .net для использования инъекции зависимостей? Я использую стандартный механизм зависимостей ядра.net. В конструкторе класса, который реализует IJob, мне нужно вводить некоторые зависимости.
.net Core Quartz Dependency Injection
Ответ 1
Вы можете использовать интерфейс Quartz.Spi.IJobFactory
и реализовать его. Кварцевые документы гласят:
Когда срабатывает триггер, связанное с ним задание создается через JobFactory, настроенный в Планировщике. По умолчанию JobFactory просто активирует новый экземпляр класса задания. Возможно, вы захотите создать свою собственную реализацию JobFactory для достижения таких целей, как наличие приложений IoC или DI-контейнера для создания/инициализации экземпляра задания. См. Интерфейс IJobFactory и связанный с ним метод Scheduler.SetJobFactory (факт).
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);
Рекомендации
Ответ 2
Вдохновленный прекрасным ответом Раббана, я создал полную реализацию JobFactory для 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 с одним заданием и работала нормально. Не стесняйтесь оставлять свои отзывы или предложения по улучшению...
Ответ 3
Не знаю, будет ли это полезно или нет, но я создал собственное расширение DI для Quartz, которое вы можете попробовать: https://github.com/JaronrH/Quartz.DependencyInjection
Короткая версия заключается в том, что вы использовали бы метод AddQuartz() для передачи в [необязательный] конфиг NaveValueCollection и [требуемый] поиск сборки Scrutor, который вы хотите (см. https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/). Например:
services.AddQuartz(s => s.FromAssemblyOf<Program>())
Этот звонок будет:
- Найти и автоматически зарегистрировать все реализации IJob, IAddScheduledJob, IAddSchedulerListener, IAddTriggerListener и IAddJobListener, найденные в сборках (Scrutor). Так что да, вы можете использовать DI в своих классах IJob таким образом!
- Установите одноэлементный IScheduler в DI, который использует вышеупомянутые ресурсы DI.
- Зарегистрируйте адаптер для IScheduler, чтобы для регистрации в Quartz использовалась Microsoft Logging.
Затем вы можете либо использовать provider.StartQuartz() для запуска планировщика (который автоматически ищет IApplicationLifetime и регистрирует планировщик для завершения работы, если доступно), либо использовать обычный DI для получения и запуска служб (provider.GetService(). Start();).
Надеюсь это поможет!