使用Quartz.NET时,向IJob注入ILogger后调度器停止工作
问题:Quartz.NET注入ILogger后调度器停止工作
在使用Quartz.NET开发后台服务时,实现IJob接口的ExtractionJob类通过构造函数注入ILogger<ExtractionJob>后,调度器完全停止工作。相关代码如下:
Program.cs
IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { IConfiguration configuration = hostContext.Configuration; AppSettings.Init(configuration.GetSection("AppSettings")); ConfigureLogger().Wait(); services.AddHostedService<Worker>(); }) .UseWindowsService() .Build(); host.Run(); async Task ConfigureLogger() { // Set up Application Insights TelemetryConfiguration string appInsightsCS = await ApiHelper.GetAppInsightsConnectionString(); if (!String.IsNullOrEmpty(appInsightsCS)) { TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault(); telemetryConfiguration.ConnectionString = appInsightsCS; Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .Enrich.FromLogContext() .Enrich.WithProperty("ApplicationName", "Extractor") .Enrich.WithProperty("Practice Id", AppSettings.Instance.PracticeId) .Enrich.WithProperty("Practice Name", AppSettings.Instance.PracticeName) .Enrich.WithProperty("PmsType", AppSettings.Instance.PmsType) .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces) .CreateLogger(); } else { Console.WriteLine("Appplication Inssights connection not found"); } }
Worker.cs
public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await new MyScheduler().Initiate(); } }
MyScheduler.cs
public class MyScheduler { public async Task Initiate() { try { ScheduleInformation scheduleInfo = await ApiHelper.LoadScheduleInfo(); if(scheduleInfo != null) { SharedDataService.PracticeConfigs = scheduleInfo.PracticeConfigs; await ScheduleJob(scheduleInfo); } } } public async Task ScheduleJob(ScheduleInformation scheduleInfo) { // Create a scheduler IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler(); // Define a job IJobDetail job = JobBuilder.Create<ExtractionJob>() .WithIdentity($"Schedule-{scheduleInfo.Id}", $"JobGroup{scheduleInfo.Id}") .Build(); //set job parameters job.JobDataMap.Put("JobParams", scheduleInfo); // Define a trigger with the retrieved cron expression ITrigger trigger = TriggerBuilder.Create() .WithIdentity($"ScheduleTrigger-{scheduleInfo.Id}", $"TriggerGroup{scheduleInfo.Id}") .StartNow() .WithCronSchedule(scheduleInfo.CronExpression) .Build(); Log.Information($"Schedule started. Id = {scheduleInfo.Id}"); // Schedule the job with the trigger await scheduler.ScheduleJob(job, trigger); await scheduler.Start(); } }
ExtractionJob.cs(注入ILogger后调度器失效)
public class ExtractionJob : IJob { readonly AzureHelper _azureHelper; readonly ILogger<ExtractionJob> _logger; public ExtractionJob(ILogger<ExtractionJob> logger) { _azureHelper = new AzureHelper(); _logger = logger; } public async Task Execute(IJobExecutionContext context) { } }
原因分析
Quartz.NET默认使用SimpleJobFactory,这个工厂只能实例化无参构造函数的作业类。当ExtractionJob添加了带ILogger参数的构造函数后,默认工厂无法创建作业实例,直接导致调度器无法正常运行。同时当前代码没有将Quartz集成到.NET的依赖注入容器中,无法利用DI容器来解析作业的依赖项。
解决方案
1. 安装Quartz的DI扩展包
确保安装Quartz.Extensions.DependencyInjection NuGet包,用于将Quartz集成到.NET DI容器。
2. 修改Program.cs,集成Quartz到DI
更新服务配置,注册Quartz服务、作业类,并配置调度器使用DI作业工厂:
IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { IConfiguration configuration = hostContext.Configuration; AppSettings.Init(configuration.GetSection("AppSettings")); ConfigureLogger().Wait(); // 注册Quartz服务 services.AddQuartz(q => { // 注册ExtractionJob到DI,供Quartz使用 q.AddJob<ExtractionJob>(j => j.WithIdentity(nameof(ExtractionJob))); // 配置Quartz使用DI容器的作业工厂 q.UseMicrosoftDependencyInjectionJobFactory(); }); // 注册Quartz托管服务,自动管理调度器生命周期 services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); // 注册MyScheduler到DI services.AddScoped<MyScheduler>(); services.AddHostedService<Worker>(); }) .UseWindowsService() .Build(); host.Run(); // 原ConfigureLogger方法保持不变 async Task ConfigureLogger() { string appInsightsCS = await ApiHelper.GetAppInsightsConnectionString(); if (!String.IsNullOrEmpty(appInsightsCS)) { TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault(); telemetryConfiguration.ConnectionString = appInsightsCS; Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .Enrich.FromLogContext() .Enrich.WithProperty("ApplicationName", "Extractor") .Enrich.WithProperty("Practice Id", AppSettings.Instance.PracticeId) .Enrich.WithProperty("Practice Name", AppSettings.Instance.PracticeName) .Enrich.WithProperty("PmsType", AppSettings.Instance.PmsType) .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces) .CreateLogger(); } else { Console.WriteLine("Appplication Inssights connection not found"); } }
3. 修改Worker.cs,通过DI注入MyScheduler
不再直接new MyScheduler,而是从DI获取实例:
public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly MyScheduler _myScheduler; public Worker(ILogger<Worker> logger, MyScheduler myScheduler) { _logger = logger; _myScheduler = myScheduler; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await _myScheduler.Initiate(stoppingToken); } }
4. 修改MyScheduler.cs,通过DI注入IScheduler
不再手动创建调度器,而是从DI获取IScheduler实例:
public class MyScheduler { private readonly IScheduler _scheduler; // 通过构造函数注入IScheduler public MyScheduler(IScheduler scheduler) { _scheduler = scheduler; } public async Task Initiate(CancellationToken stoppingToken) { try { ScheduleInformation scheduleInfo = await ApiHelper.LoadScheduleInfo(); if(scheduleInfo != null && !stoppingToken.IsCancellationRequested) { SharedDataService.PracticeConfigs = scheduleInfo.PracticeConfigs; await ScheduleJob(scheduleInfo, stoppingToken); } } catch (Exception ex) { Log.Error(ex, "Failed to initiate scheduler"); } } public async Task ScheduleJob(ScheduleInformation scheduleInfo, CancellationToken stoppingToken) { // 从DI获取的调度器已经启动(由QuartzHostedService管理),无需手动Start // Define a job,使用已注册的作业类型 IJobDetail job = JobBuilder.Create<ExtractionJob>() .WithIdentity($"Schedule-{scheduleInfo.Id}", $"JobGroup{scheduleInfo.Id}") .Build(); // 设置作业参数 job.JobDataMap.Put("JobParams", scheduleInfo); // 定义触发器 ITrigger trigger = TriggerBuilder.Create() .WithIdentity($"ScheduleTrigger-{scheduleInfo.Id}", $"TriggerGroup{scheduleInfo.Id}") .StartNow() .WithCronSchedule(scheduleInfo.CronExpression) .Build(); Log.Information($"Schedule started. Id = {scheduleInfo.Id}"); // 调度作业 await _scheduler.ScheduleJob(job, trigger, stoppingToken); } }
5. 保持ExtractionJob不变(依赖注入正常生效)
public class ExtractionJob : IJob { readonly AzureHelper _azureHelper; readonly ILogger<ExtractionJob> _logger; public ExtractionJob(ILogger<ExtractionJob> logger) { _azureHelper = new AzureHelper(); _logger = logger; } public async Task Execute(IJobExecutionContext context) { // 可以正常使用_logger进行日志记录 _logger.LogInformation("ExtractionJob executed"); // 业务逻辑代码 } }
内容的提问来源于stack exchange,提问作者Vishal Suthar
相关产品推荐
相关产品推荐

