Schedule Background Job using Quartz.NET
You may have encountered a situation where you must do some backend job without user intervention. For example, in an IOT application, your application needs to receive frequently published data from devices or send scheduler values to devices. .NET provides background job libraries for such tasks. I will discuss one of the background libraries, Quartz.NET, with a coding example. I will break down Quartz for you with simple, practical examples.
What is Quartz.NET
Quartz.NET is an open-source .NET library for scheduling and automating tasks within applications. It also allows you to schedule recurring jobs, such as alarming the clock, retrieving device data, sending push notifications, etc. Quartz can be broken down into its key concepts.
- Job: A job is a task or unit of work you want to run in the background, such as alarming the clock or retrieving device data. The job class contains all your job logic inside the Execute method by implementing the IJob interface.
- Trigger: The trigger defines the frequency at which the job should be executed. It specifies the timing and frequency of job execution. Triggers can be SimpleTrigger (for one-time execution), CronTrigger (for more complex schedules using Cron expressions), and more.
- Scheduler: The scheduler provides coordination between jobs and their triggers. It manages starting, stopping, and rescheduling your jobs based on their triggers.
Let's move towards implementing Quartz.NET in our application. To keep things simple and understandable, I will provide two examples for each trigger: simple and cron. For the first example, let's create a C# console application.
Example 1: Simple Quartz job in a Console application
Step 1: Install the Quartz.NET Nuget package
Run the following command to install the package:
dotnet add package Quartz
Step 2: Create a Job Class
Creating a job class defining our job by implementing the IJob
interface:
I created a separate folder for jobs and defined the PrintJob
class:
using Quartz;
namespace QuartzConsole.Jobs
{
public class PrintJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine($"Hello, World! The time is: {DateTime.Now}");
return Task.CompletedTask;
}
}
}
Step 3: Set Up Quartz scheduler in the Program.cs
file
using Quartz;
using Quartz.Impl;
using QuartzConsole.Jobs;
// Create a scheduler factory
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
// Get a scheduler
IScheduler scheduler = await schedulerFactory.GetScheduler();
// Create a job
IJobDetail job = JobBuilder.Create<PrintJob>()
.WithIdentity("printJob", "printGroup")
.Build();
// Create a trigger that fires every 5 seconds
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("printTrigger", "printGroup")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(5)
.RepeatForever())
.Build();
// Schedule the job using the trigger
await scheduler.ScheduleJob(job, trigger);
// Start the scheduler
await scheduler.Start();
Console.WriteLine("Press [Enter] to exit.");
Console.ReadLine();
// Shutdown the scheduler on exit
await scheduler.Shutdown();
The code sets a trigger name using the WithIdentity
method. The trigger is started by calling StartNow
. The call to WithIntervalInSeconds(5)
specifies the interval between repetitions and RepeatForever()
defines frequency.
Step 4: Run the application
Example 2: Alarm job in ASP.NET Core API
You can also implement a Quartz job in ASP.NET Core. We consider a simple alarm clock with a crone job in our API to be executed automatically at 7 AM daily. The following steps are involved in achieving it.
Step 1: Add the Quartz NuGet packages to the project
dotnet add package Quartz
dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions
Step 2: Add Quartz data in appsettings.json
{
"Quartz": {
"CronSchedule": "0 0 7 * * ?",
"JobName": "AlarmJob",
"JobGroup": "AlarmGroup"
}
}
Step 3: Create an alarm job by implementing IJob
using Quartz;
using System;
using System.Threading.Tasks;
public class AlarmJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine("🔔 Alarm! It's 7:00 AM. Wake up!");
return Task.CompletedTask;
}
}
Step 4: Define a background job for Quartz
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Quartz;
using System.Threading;
using System.Threading.Tasks;
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
// Quartz setting to read configured value from appsetting
private readonly IOptions<QuartzSettings> _quartzSettings;
private readonly IJobFactory _jobFactory;
private IScheduler _scheduler;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
IOptions<QuartzSettings> quartzSettings)
{
_schedulerFactory = schedulerFactory;
_jobFactory = jobFactory;
_quartzSettings = quartzSettings;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_scheduler = await _schedulerFactory.GetScheduler();
_scheduler.JobFactory = _jobFactory;
var jobDetail = JobBuilder.Create<AlarmJob>()
.WithIdentity(_quartzSettings.Value.JobName, _quartzSettings.Value.JobGroup)
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity($"{_quartzSettings.Value.JobName}Trigger", _quartzSettings.Value.JobGroup)
.WithCronSchedule(_quartzSettings.Value.CronSchedule) .WithMisfireHandlingInstructionFireNow())
.Build();
await _scheduler.ScheduleJob(jobDetail, trigger);
await _scheduler.Start();
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await _scheduler?.Shutdown();
}
}
Trigger has a name and group with WithIdentity
. We defined the CRON trigger with WithCronSchedule()
as the alarm is a repeating job. WithMisfireHandlingInstructionFireNow()
is a retry strategy that immediately re-runs the job if it fails.
Step 5: Inject the quartz job in Program.cs
// Bind Quartz settings from appsettings.json builder.Services.Configure<QuartzSettings>(builder.Configuration.GetSection("Quartz"));
builder.Services.AddSingleton<IJobFactory, SingletonJobFactory>();
builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
builder.Services.AddSingleton<AlarmJob>(); // Register AlarmJob
// Register a hosted service for Quartz
builder.Services.AddHostedService<QuartzHostedService>();
Step 6: Run the project. At 7 AM, it runs the job and prints
Why to use Quartz in your project?
Quartz offers many benefits, making it the best fit for your application's background jobs. As the library is open-source, it is a mature codebase backed by a strong community. Its continuous support makes it reliable for production use. Moreover, Quartz has great time precision enabling you to control timely execution granularly. In case of job failure, the library provides robustness in the form of a retry strategy. Besides these features, integrating the background services and the library itself is up to modern standards. You can use it with ASP.NET Core's dependency injection, similar to your other services.
Conclusion
Background jobs are essential in many situations in your application. You will need to schedule some executions, such as alarm notifications, emailing, device data retrieval, etc., automatically. These tasks cannot be done with user interventions but need timed automation. The Quartz.NET library provides a handy way to implement background jobs for .NET applications. We elaborated on this tool in-depth, along with simple examples. Its reliability, integration ease, and continuous support make it an ideal candidate for your next background job implementation.
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website