Quartz.Net Работа с несколькими уровнями DI - PullRequest
1 голос
/ 24 июня 2019

У меня есть JobFactory : IJobFactory, который работает.Я могу просто создавать запланированные задания и без проблем вставлять простые зависимости, но у меня есть проблема с одной проблемой.Одна из зависимостей сама зависит от HttpClient, но это, похоже, не работает.

Например,

services.AddHttpClient<Dependency>("dependency");
services.AddSingleton(typeof(Dependency));

using (var serviceProvider = services.BuildServiceProvider())
{
  var schedulerFactory = new StdSchedulerFactory();
  var scheduler = await schedulerFactory.GetScheduler();
  scheduler.JobFactory = new JobFactory(serviceProvider);
  await scheduler.Start();

  var job = JobBuilder.Create<Job1>()
                .WithIdentity("job1")
                .Build();
  var trigger = TriggerBuilder.Create()
                .WithIdentity("trigger1")
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(1)
                    .RepeatForever())
                .Build();
  await scheduler.ScheduleJob(job, trigger);
}

с конструктором зависимости

public Dependency(HttpClient httpClient)
{
  // <snipped>
}

и конструктор задания:

public Job1(Dependency dependency)
{
  // <snipped>
}

Когда я пытаюсь запустить это, мне говорят, что задание выдает необработанное исключение.Отладка через мой JobFactory говорит мне, что httpClient вообще не вводится.Разве .AddHttpClient не должен обрабатывать это?Разве это не работает из-за нескольких уровней DI?Можно ли сделать по-другому?

Обратите внимание, я попытался вручную зарегистрировать Job1, как я сделал Dependency, но это не решило проблему.

Ответы [ 2 ]

1 голос
/ 24 июня 2019

Этот код созрел для рефакторинга.

Поместите планировщик в IHostedService и позвольте ему обработать запуск планировщика.

public interface IHostedService {
    //
    // Summary:
    //     Triggered when the application host is ready to start the service.
    Task StartAsync(CancellationToken cancellationToken);
    //
    // Summary:
    //     Triggered when the application host is performing a graceful shutdown.
    Task StopAsync(CancellationToken cancellationToken);
}

public class SchedulerService : IHostedService {
    readonly IJobFactory jobFactory;
    readonly ISchedulerFactory schedulerFactory
    IScheduler  scheduler;

    public SchedulerService(IJobFactory jobFactory, ISchedulerFactory schedulerFactory) {
        this.jobFactory = jobFactory;
        this.schedulerFactory = schedulerFactory;
    }

    public async Task StartAsync(CancellationToken cancellationToken) {
        scheduler = await schedulerFactory.GetScheduler();
        scheduler.JobFactory = jobFactory;

        IJobDetail job = JobBuilder.Create<Job1>()
            .WithIdentity("job1")
            .Build();

        ITrigger  trigger = TriggerBuilder.Create()
            .WithIdentity("trigger1")
            .StartNow()
            .WithSimpleSchedule(x => x.WithIntervalInSeconds(1)
                .RepeatForever())
            .Build();

        await scheduler.ScheduleJob(job, trigger);

        await scheduler.Start(cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken) {
        return scheduler.Shutdown(cancellationToken);
    }
}

С этим в сторонетеперь настройку можно выполнить без проблем при запуске, добавив все типы в коллекцию служб

class Program {
    static async Task Main(string[] args) {

        var services = new ServiceCollection();

        //...

        services.AddHttpClient<IDependency, Dependency>();
        services.AddScoped<Job1>();
        services.AddTransient<ISchedulerFactory, StdSchedulerFactory>();
        services.AddTransient<IJobFactory>(serviceProvider => new JobFactory(serviceProvider)); 
        services.AddTransient<IHostedService, SchedulerService>();

        //...

        IServiceProvider serviceProvider = services.BuildServiceProvider();

        var service = serviceProvider.GetRequiredService<IHostedService>(); 
        await service.StartAsync();

        Console.ReadKey();

    }
}

Так что теперь служба будет управлять запуском, а также будут добавляться все необходимые зависимости по мере необходимости.

Включая класс Typed Client Dependency, предполагая следующее

public class Dependency : IDependency {

    public Dependency(HttpClient httpClient) {
      // <snipped>
    }
}

public class Job1: IJob {
    public Job1(IDependency dependency) {
      // <snipped>
    }
}
0 голосов
/ 26 июня 2019

Оказывается, у меня было три отдельных и не связанных друг с другом проблемы одновременно:

  1. AddHttpClient<Dependency>() регистрирует мою зависимость, и я не должен также включать AddSingleton<Dependency>().
  2. Iнужно было зарегистрировать саму работу: services.AddScoped<Job1>(); (спасибо, @Nkosi)
  3. У моей работы был конструктор internal.Это должно было быть public.(Возможно, потому что static async Task Main(string[] args) является статичным, но я не уверен.)
...