Roman
Roman

Reputation: 161

ASP .Net Core with Quartz.NET 3.4.0

I use Quartz.NET with ASP .Net Core. I want to create scheduler for my app business logic (HelloWorldJob=)), …which can started to execute by event, can paused and resumed by user commands. But something is going wrong, possibly from the beginning(.

So, I created class QuartzHostedService class. In it I configurated job, trigger and scheduler for my business logic. QuartzHostedService public async Task StartAsync(CancellationToken cancellationToken) ” is being executed only once, and started _scheduler with my job - HelloWorldJob. But! The job was never executed! Why?

public class QuartzHostedService : IHostedService
{
    private readonly ISchedulerFactory _schedulerFactory;
    public IScheduler? _scheduler;
    private readonly ILogger<QuartzHostedService> _logger;
    private readonly InitializationModuleConfiguration _initializationModuleConfiguration;
    private  IJobDetail _jobDetail;
    private  bool isEnabled;


    public QuartzHostedService(
        ISchedulerFactory schedulerFactory,
        InitializationModuleConfiguration initializationModuleConfiguration,
        ILogger<QuartzHostedService> logger
        )
    {
        _logger =    logger;
        _schedulerFactory = schedulerFactory;
        _initializationModuleConfiguration = initializationModuleConfiguration;
    }


    public async Task StartAsync(CancellationToken cancellationToken)
    {
        //StdSchedulerFactory factory = new StdSchedulerFactory();

        _scheduler = await _schedulerFactory.GetScheduler();

        await _scheduler.Start();

        // define the job and tie it to our HelloWorldJob class
        _jobDetail = JobBuilder.Create<HelloWorldJob>()
        .WithIdentity("_job", "group")
        .Build();

        ////When you wish to schedule a job, you instantiate a trigger
        ////and 'tune' its properties to provide the scheduling you wish to have.
        ITrigger _trigger = TriggerBuilder.Create()
        .WithIdentity("_trigger", "group")
        .StartNow()
            .WithSimpleSchedule(x => x
            .WithIntervalInSeconds(5)
            .RepeatForever())
        .Build();


        if (_initializationModuleConfiguration.DataLoaderStarted && !isEnabled)
        {
            // Tell quartz to schedule the job using our trigger
            await _scheduler.ScheduleJob(_jobDetail, _trigger);
            isEnabled = true;
        }
        _logger.LogInformation("Execute");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}
public class Startup
{
     .......
     .......
        services.AddQuartzHostedService(
            quartz => quartz.WaitForJobsToComplete = true);

        services.AddSingleton<Quartz.ISchedulerFactory, StdSchedulerFactory>();
        services.AddSingleton<HelloWorldJob>();
        services.AddHostedService<QuartzHostedService>();

[DisallowConcurrentExecution]
public class HelloWorldJob: IJob, IHelloWorldJob
{
    private readonly ILogger<HelloWorldJob> _logger;
    public HelloWorldJob(ILogger<HelloWorldJob> logger)
    {
        _logger = logger;
    }

    public Task Execute(IJobExecutionContext context)
    {
        _logger.LogInformation("Hello world!");
        return Task.CompletedTask;
    }
}

Upvotes: 2

Views: 2340

Answers (2)

Roman
Roman

Reputation: 161

Corrected Startup, now it works:

Startup

        services.AddQuartz(q =>
        {
            q.UseDefaultThreadPool(maxConcurrency: 1);
            q.UseMicrosoftDependencyInjectionJobFactory();
        });
        services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

        services.AddSingleton<IDataProcessingModule,DataProcessingModule>();
        services.AddSingleton<IDataLoaderModule, DataLoaderModule>();
        services.AddSingleton<IKafkaSendingModule, KafkaSendingModule>();

QuartzHostedService

    public QuartzHostedService(
        ISchedulerFactory schedulerFactory,
        InitializationModuleConfiguration initializationModuleConfiguration,
        ILogger<QuartzHostedService> logger,
        DataLoaderConfiguration dataLoaderConfiguration,
        DataProcessingConfiguration dataProcessingConfiguration
        )
    {
        _logger = logger;
        _schedulerFactory = schedulerFactory;
        _initializationModuleConfiguration = initializationModuleConfiguration;
        _dataLoaderConfiguration = dataLoaderConfiguration;
        _dataProcessingConfiguration = dataProcessingConfiguration;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _cancellationToken = cancellationToken;//???
        _scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
        await _scheduler.Start(cancellationToken);

        _jobDetailLoader = CreateJob<IDataLoaderModule>();
        _jobDetailProcessing = CreateJob<IDataProcessingModule>();

        if (_initializationModuleConfiguration.DataLoaderStarted)
        {
            await StartJobAsync<IDataLoaderModule>(_dataLoaderConfiguration.LoaderTimeoutMs);
        }

        if (_initializationModuleConfiguration.DataProcessingStarted)
        {
            await StartJobAsync<IDataProcessingModule>(_dataLoaderConfiguration.LoaderTimeoutMs);
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await _scheduler.Shutdown(cancellationToken);
    }

    private IJobDetail CreateJob<TModule>() where TModule : IJob
    {
        var job = JobBuilder
            .Create<TModule>()
            .WithIdentity(typeof(TModule).Name, "group")
            .StoreDurably()
            .Build();
           job.JobDataMap.Put("Status", "Created");

        return job;
    }

    private ITrigger CreateTrigger<TModule>(int interval) where TModule : IJob
    {
        return TriggerBuilder
            .Create()
            .WithIdentity(typeof(TModule).Name, "group")
            .ForJob(typeof(TModule).Name, "group")
            .StartNow()
            .WithSimpleSchedule(x => x
               .WithInterval(TimeSpan.FromMilliseconds(interval))
               .RepeatForever())
            .Build();

    }

    public async Task StartJobAsync<TModule>(int interval) where TModule : IJob
    {
        try
        {
            var schedJobs = GetSchedJob(typeof(TModule).Name);
            var status = schedJobs.JobDetail.JobDataMap.GetString("Status");
            
            if (status == "Created")
            {
                var trigger = CreateTrigger<TModule>(interval);
                await _scheduler.ScheduleJob(schedJobs.JobDetail, trigger, _cancellationToken); //cancellationToken
                schedJobs.JobDetail.JobDataMap.Put("Status", "Runned");
            }
            await _scheduler.ResumeJob(schedJobs.JobDetail.Key, _cancellationToken);
        }
        catch (SchedulerException e)
        {
            _logger.LogError(e.ToString());
        }
    }

    public async Task PauseJobAsync<TModule>() where TModule : IJob
    {
        try
        {
            var schedJobs = GetSchedJob(typeof(TModule).Name);
            await _scheduler.PauseJob(schedJobs.JobDetail.Key, _cancellationToken);
            schedJobs.JobDetail.JobDataMap.Put("Status", "Stopped");
        }
        catch (SchedulerException e)
        {
            _logger.LogError(e.ToString());
        }
    }

    private (IScheduler Scheduler, IJobDetail JobDetail) GetSchedJob(string key)
    {
        IJobDetail job = key == nameof(IDataLoaderModule) ? _jobDetailLoader
            : key == nameof(IDataProcessingModule) ? _jobDetailProcessing
            : _jobDetailProcessing;
        return (_scheduler, job);
    }
}

}

Upvotes: 0

Chen
Chen

Reputation: 5144

Your code logic looks a bit messy, you can refer to my test code below (Since I don't know the logic of your InitializationModuleConfiguration, I didn't inject this one):

QuartzHostedService:

public class QuartzHostedService : IHostedService
{
    private readonly ISchedulerFactory _schedulerFactory;
    private readonly IJobFactory _jobFactory;

    public QuartzHostedService(
        ISchedulerFactory schedulerFactory,
        IJobFactory jobFactory)
    {
        _schedulerFactory = schedulerFactory;
        _jobFactory = jobFactory;
    }
    public IScheduler Scheduler { get; set; }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
        Scheduler.JobFactory = _jobFactory;

        var job = CreateJob();
        var trigger = CreateTrigger();
        await Scheduler.ScheduleJob(job, trigger, cancellationToken);
        await Scheduler.Start(cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await Scheduler?.Shutdown(cancellationToken);
    }

    private static IJobDetail CreateJob()
    {
            
        return JobBuilder
            .Create<HelloWorldJob>()
            .WithIdentity("_job", "group")
            .Build();
    }

    private static ITrigger CreateTrigger()
    {
        return TriggerBuilder
            .Create()
            .WithIdentity("_trigger", "group")
            .StartNow()
            .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
            .Build();
    }
}

Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddQuartz(q =>
    {
        q.UseMicrosoftDependencyInjectionScopedJobFactory();
    });
    services.AddQuartzHostedService(
        quartz => quartz.WaitForJobsToComplete = true);

    services.AddSingleton<Quartz.ISchedulerFactory, StdSchedulerFactory>();
    services.AddSingleton<HelloWorldJob>();
    services.AddHostedService<QuartzHostedService>();
}

HelloWorldJob:

[DisallowConcurrentExecution]
public class HelloWorldJob : IJob
{
    private readonly ILogger<HelloWorldJob> _logger;
    public HelloWorldJob(ILogger<HelloWorldJob> logger)
    {
        _logger = logger;
    }

    public Task Execute(IJobExecutionContext context)
    {
        _logger.LogInformation("Hello world!");
        return Task.CompletedTask;
    }
}

Test Result:

enter image description here

Hope this can help you.

Upvotes: 1

Related Questions