.net Core Quartz Dependency Injection

Как настроить кварц в ядре .net для использования инъекции зависимостей? Я использую стандартный механизм зависимостей ядра.net. В конструкторе класса, который реализует IJob, мне нужно вводить некоторые зависимости.

Ответ 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);

Рекомендации

  1. Кварцевая документация

  2. Api

Ответ 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();).

Надеюсь это поможет!