You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:04:52