Thank you to our sponsors who keep this newsletter free to the reader:
Today's issue is sponsored by Treblle. Treblle is a lightweight SDK that helps engineering and product teams build, ship & maintain REST-based APIs faster. Simple integration for all popular languages & frameworks, including .NET 6.
And by IcePanel. IcePanel is a collaborative C4 model modelling & diagramming tool that helps explain complex software systems. With an interactive map, you can align your software engineering & product teams on technical decisions across the business.
If you're building a scalable application, it's a common requirement to offload some work in your application to a background job.
Here are a few examples of that:
- Publishing email notifications
- Generating reports
- Updating a cache
- Image processing
How can you create a recurring background job in .NET?
Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems.
There are three concepts you need to understand in Quartz.NET:
- Job - the actual background task you want to run
- Trigger - the trigger controlling when a job runs
- Scheduler - responsible for coordinating jobs and triggers
Let's see how we can use Quartz.NET to create and schedule background jobs.
The first thing we need to do is install the Quartz.NET NuGet package.
There are a few to pick from, but we're going to install the
The reason we're using this library is because it integrates nicely with .NET using an
To get the Quartz.NET hosted service up and running, we need two things:
- Add the required services with the DI container
- Add the hosted service
options.WaitForJobsToComplete = true;
Quartz.NET will create jobs by fetching them from the DI container. This also means you can use scoped services in your jobs, not just singleton or transient services.
WaitForJobsToComplete option to
true will ensure that Quartz.NET waits for the jobs to complete gracefully before exiting.
To crate a background job with Quartz.NET you need to implement the
It only exposes a single method -
Execute - where you can place the code for your background job.
A few things worth noting here:
- We're using DI to inject the
- The job is decorated with
DisallowConcurrentExecutionto prevent running the same job concurrently
public class ProcessOutboxMessagesJob : IJob
private readonly ApplicationDbContext _dbContext;
private readonly IPublisher _publisher;
_dbContext = dbContext;
_publisher = publisher;
public async Task Execute(IJobExecutionContext context)
List<OutboxMessage> messages = await _dbContext
.Where(m => m.ProcessedOnUtc == null)
foreach (OutboxMessage outboxMessage in messages)
IDomainEvent? domainEvent = JsonConvert
TypeNameHandling = TypeNameHandling.All
if (domainEvent is null)
await _publisher.Publish(domainEvent, context.CancellationToken);
outboxMessage.ProcessedOnUtc = DateTime.UtcNow;
Now that the background job is ready, we need to register it with the DI container and add a trigger that will run the job.
I mentioned at the start that there are three key concepts in Quartz.NET:
We already implemented the
ProcessOutboxMessagesJob background job in the previous section.
The Quartz.NET library will take care of the scheduler.
And this leaves us with configuring the trigger for our
var jobKey = new JobKey(nameof(ProcessOutboxMessagesJob));
trigger => trigger.ForJob(jobKey).WithSimpleSchedule(
schedule => schedule.WithIntervalInSeconds(10).RepeatForever()));
We need to uniquely identify our background job with a
I like to keep it simple and use the job name.
AddJob will register the
ProcessOutboxMessagesJob with DI and also with Quartz.
After that we configure a trigger for this job by calling
You need to associate the job with the trigger by calling
ForJob, and then you configure the schedule for the background job.
In this example, I'm scheduling the job to run every ten seconds and repeat forever while the hosted service is running.
Quartz also has support for configuring triggers using cron expressions.
By default, Quartz configures all jobs using the
RAMJobStore which is the most performant because it keeps all of its data in RAM.
However, this also means it's volatile and you can lose all scheduling information when your application stops or crashes.
It could be useful to have a persistent job store in some scenarios and there's a built in
AdoJobStore which works with SQL databases.
You need to create a set of database tables for Quartz.NET to use.
You can learn more about this in the job stores documentation.
Quartz.NET makes running background jobs in .NET easy, and you can use all the power of DI in your background jobs. It's also flexible for various scheduling requirements with configuration via code or using cron expressions.
There's some room for improvement to make scheduling jobs easier and reduce boilerplate:
- Add an extension method to simplify configuring jobs with a simple schedule
- Add an extension method to simplify configuring jobs with a cron schedule from application settings
If you want to see a tutorial on using Quartz.NET, I made an in-depth video about using Quartz for processing Outbox messages.
That's all for this week.
See you next Saturday.