Scheduling Jobs with Quartz.NET in ASP.NET Core
By FoxLearn 1/3/2025 4:45:47 AM 97
It is a .NET port of the popular Java-based Quartz framework and offers robust support for Cron expressions.
Create an ASP.NET Core API project
To create an ASP.NET Core API project in Visual Studio, follow these steps:
- Open Visual Studio and click on "Create new project."
- In the "Create new project" window, select "ASP.NET Core Web Application" from the templates list, then click Next.
- Specify the project name and location in the next window and click Create.
- Choose .NET Core as the runtime and select ASP.NET Core 2.2 (or later), with ASP.NET Core 8.0 as the version.
- Select the "API" project template and ensure that "Enable Docker Support" and "Configure for HTTPS" are unchecked. Set Authentication to "No Authentication."
- Click Create to generate the project.
- In Solution Explorer, right-click on the Controllers folder and select "Add -> Controller" to create a new controller, such as DefaultController.
To use Quartz, install the Quartz package from NuGet. You can do this through the Visual Studio NuGet package manager or by running the following command in the NuGet package manager console:
Install-Package Quartz
Quartz.NET jobs, triggers, and schedulers
In Quartz.NET, the key concepts are jobs, triggers, and schedulers. A job contains the task to be executed and is represented by a class implementing the IJob interface. A trigger defines the schedule and execution details for a job. The scheduler manages polling and running jobs according to the predefined schedules.
How to Create a scheduler using Quartz.NET?
You can have multiple schedulers in an application, but for simplicity, we'll use one.
To create a scheduler instance, use the following code:
var scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult();
After creating the scheduler, add it as a singleton service in the ConfigureServices
method of the Startup.cs
file.
services.AddSingleton(scheduler);
How to Start and stop a scheduler using Quartz.NET?
To start and stop the scheduler, create a class that implements the IHostingService
interface. This allows you to manage the scheduler's lifecycle within the application.
public class CustomQuartzHostedService : IHostedService { private readonly IScheduler _scheduler; public CustomQuartzHostedService(IScheduler scheduler) { _scheduler = scheduler; } public async Task StartAsync(CancellationToken cancellationToken) { await _scheduler?.Start(cancellationToken); } public async Task StopAsync(CancellationToken cancellationToken) { await _scheduler?.Shutdown(cancellationToken); } }
You should register the hosted service in the services collection within the ConfigureServices
method using the appropriate code snippet.
services.AddHostedService<QuartzHostedService>();
Here is the updated ConfigureServices
method for reference:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); var scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult(); services.AddSingleton(scheduler); services.AddHostedService<QuartzHostedService>(); }
How to Create a job using Quartz.NET
A job in Quartz.NET is a class that implements the IJob
interface and contains an Execute()
method, which takes an IJobExecutionContext
instance. The following code example shows a job class with an asynchronous Execute()
method, where the task to be performed is defined.
[DisallowConcurrentExecution] public class NotificationJob : IJob { private readonly ILogger<NotificationJob> _logger; public NotificationJob(ILogger<NotificationJob> logger) { _logger = logger; } public Task Execute(IJobExecutionContext context) { _logger.LogInformation("Hello world!"); return Task.CompletedTask; } }
How to Create a job factory using Quartz.NET
A job factory is a class that implements the IJobFactory
interface, including the NewJob()
and ReturnJob()
methods. The following code snippet demonstrates how to create a factory class that generates and returns job instances.
public class CustomQuartzJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public CustomQuartzJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle triggerFiredBundle, IScheduler scheduler) { var jobDetail = triggerFiredBundle.JobDetail; return (IJob)_serviceProvider.GetService(jobDetail.JobType); } public void ReturnJob(IJob job) { } }
This implementation doesn't utilize job pooling. To enable job pooling, you need to modify the NewJob()
method and implement the ReturnJob()
method.
How to Create a JobMetadata class to store your job metadata
A custom class will be used to store job metadata, such as job ID, name, and other details. The following class represents this job metadata.
public class JobMetadata { public Guid JobId { get; set; } public Type JobType { get; } public string JobName { get; } public string CronExpression { get; } public JobMetadata(Guid Id, Type jobType, string jobName, string cronExpression) { JobId = Id; JobType = jobType; JobName = jobName; CronExpression = cronExpression; } }
How to Create a hosted service to start and stop the Quartz.NET scheduler
A hosted service is a class that implements the IHostedService
interface and starts the Quartz scheduler. The following code example shows how to create a custom hosted service class.
public class CustomQuartzHostedService : IHostedService { private readonly ISchedulerFactory schedulerFactory; private readonly IJobFactory jobFactory; private readonly JobMetadata jobMetadata; public CustomQuartzHostedService(ISchedulerFactory schedulerFactory, JobMetadata jobMetadata, IJobFactory jobFactory) { this.schedulerFactory = schedulerFactory; this.jobMetadata = jobMetadata; this.jobFactory = jobFactory; } public IScheduler Scheduler { get; set; } public async Task StartAsync(CancellationToken cancellationToken) { Scheduler = await schedulerFactory.GetScheduler(); Scheduler.JobFactory = jobFactory; var job = CreateJob(jobMetadata); var trigger = CreateTrigger(jobMetadata); await Scheduler.ScheduleJob(job, trigger, cancellationToken); await Scheduler.Start(cancellationToken); } public async Task StopAsync(CancellationToken cancellationToken) { await Scheduler?.Shutdown(cancellationToken); } private ITrigger CreateTrigger(JobMetadata jobMetadata) { return TriggerBuilder.Create() .WithIdentity(jobMetadata.JobId.ToString()) .WithCronSchedule(jobMetadata.CronExpression) .WithDescription($"{jobMetadata.JobName}") .Build(); } private IJobDetail CreateJob(JobMetadata jobMetadata) { return JobBuilder .Create(jobMetadata.JobType) .WithIdentity(jobMetadata.JobId.ToString()) .WithDescription($"{jobMetadata.JobName}") .Build(); } }
The following code shows the complete ConfigureServices
method in the Startup
class:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSingleton<IJobFactory, CustomQuartzJobFactory>(); services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); services.AddSingleton<NotificationJob>(); services.AddSingleton(new JobMetadata(Guid.NewGuid(), typeof(NotificationJob), "Notification Job", "0/10 * * * * ?")); services.AddHostedService<CustomQuartzHostedService>(); }
Once this is done, running the application will trigger the Execute()
method of the NotificationJob
class every 10 seconds.
Quartz.NET is an excellent choice for implementing schedulers in applications, and it also offers a persistence feature to store jobs in databases like SQL Server, PostgreSQL, or SQLite.
- Content Negotiation in Web API
- How to fix 'InvalidOperationException: Scheme already exists: Bearer'
- How to fix System.InvalidOperationException: Scheme already exists: Identity.Application
- Add Thread ID to the Log File using Serilog
- Handling Exceptions in .NET Core API with Middleware
- InProcess Hosting in ASP.NET Core
- Limits on ThreadPool.SetMinThreads and SetMaxThreads
- Controlling DateTime Format in JSON Output with JsonSerializerOptions