【问题标题】:Quartz.Net Job with multiple levels of DI具有多级 DI 的 Quartz.Net 作业
【发布时间】:2019-06-23 23:52:40
【问题描述】:

我有一个有效的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 的多个级别而不起作用?有没有办法换一种方式?

注意,我也尝试像 Dependency 一样手动注册 Job1,但这并没有解决问题。

【问题讨论】:

  • 为什么要添加单例?
  • @Nkosi,我添加了单例,以便在安排Job1 时将其注入Job1 的构造函数中。
  • 这是用于 Web 还是控制台应用程序?
  • 控制台应用程序,但它只是独立运行,在不同服务之间传递信息。几乎没有 UI。
  • 好的,那么我将不得不稍微改变我的答案,因为我认为这是托管在 Web 服务中的。

标签: c# dependency-injection .net-core quartz.net


【解决方案1】:

这段代码可以重构了。

将调度程序放在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>
    }
}

【讨论】:

  • 谢谢;这很有帮助。我有两个不相关的问题,三个不同的问题。您的解决方案帮助我意识到我需要services.AddScoped&lt;Job1&gt;();
【解决方案2】:

原来我同时遇到了三个独立且不相关的问题:

  1. AddHttpClient&lt;Dependency&gt;() 注册了我的依赖项,我不应该也包括 AddSingleton&lt;Dependency&gt;()
  2. 我需要自己注册工作:services.AddScoped&lt;Job1&gt;();(谢谢@Nkosi)
  3. 我的工作有一个internal 构造函数。它必须是public。 (可能是因为static async Task Main(string[] args) 是静态的,但我不确定。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多