dotnet-quartz-background-jobs
Generates scheduled background jobs using Quartz.NET. Includes job definitions, triggers, cron scheduling, dependency injection, and persistent job store configuration.
git clone --depth 1 https://github.com/ronnythedev/dotnet-clean-architecture-skills /tmp/dotnet-quartz-background-jobs && cp -r /tmp/dotnet-quartz-background-jobs/skills/15-dotnet-quartz-background-jobs ~/.claude/skills/dotnet-quartz-background-jobsSKILL.md
# Background Job Generator (Quartz)
## Overview
Quartz.NET is a full-featured job scheduling library:
- **Job scheduling** - Run tasks at specific times or intervals
- **Cron expressions** - Complex scheduling patterns
- **Persistence** - Jobs survive application restarts
- **Dependency injection** - Full DI support
- **Clustering** - Distributed job execution
## Quick Reference
| Component | Purpose |
|-----------|---------|
| `IJob` | Job interface to implement |
| `IConfigureOptions<QuartzOptions>` | Job registration |
| `JobKey` | Unique job identifier |
| `TriggerBuilder` | Defines when job runs |
| `CronScheduleBuilder` | Cron-based scheduling |
| `SimpleScheduleBuilder` | Interval-based scheduling |
---
## Job Structure
```
/Infrastructure/
├── BackgroundJobs/
│ ├── {JobName}Job.cs
│ ├── {JobName}JobSetup.cs
│ └── ...
└── DependencyInjection.cs
```
---
## Template: Simple Interval Job
```csharp
// src/{name}.infrastructure/BackgroundJobs/ProcessPendingOrdersJob.cs
using Microsoft.Extensions.Logging;
using Quartz;
namespace {name}.infrastructure.backgroundjobs;
/// <summary>
/// Processes pending orders every 5 minutes
/// </summary>
[DisallowConcurrentExecution] // Prevent overlapping executions
public sealed class ProcessPendingOrdersJob : IJob
{
private readonly IOrderRepository _orderRepository;
private readonly IOrderProcessor _orderProcessor;
private readonly ILogger<ProcessPendingOrdersJob> _logger;
public ProcessPendingOrdersJob(
IOrderRepository orderRepository,
IOrderProcessor orderProcessor,
ILogger<ProcessPendingOrdersJob> logger)
{
_orderRepository = orderRepository;
_orderProcessor = orderProcessor;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Starting pending orders processing...");
try
{
var pendingOrders = await _orderRepository
.GetPendingOrdersAsync(context.CancellationToken);
_logger.LogInformation(
"Found {Count} pending orders to process",
pendingOrders.Count);
foreach (var order in pendingOrders)
{
try
{
await _orderProcessor.ProcessAsync(order, context.CancellationToken);
_logger.LogInformation(
"Processed order {OrderId}",
order.Id);
}
catch (Exception ex)
{
_logger.LogError(
ex,
"Failed to process order {OrderId}",
order.Id);
}
}
_logger.LogInformation("Completed pending orders processing");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in pending orders processing job");
throw; // Quartz will handle retry based on configuration
}
}
}
```
---
## Template: Job Setup (IConfigureOptions)
```csharp
// src/{name}.infrastructure/BackgroundJobs/ProcessPendingOrdersJobSetup.cs
using Microsoft.Extensions.Options;
using Quartz;
namespace {name}.infrastructure.backgroundjobs;
internal sealed class ProcessPendingOrdersJobSetup
: IConfigureOptions<QuartzOptions>
{
public void Configure(QuartzOptions options)
{
var jobKey = JobKey.Create(nameof(ProcessPendingOrdersJob));
options
.AddJob<ProcessPendingOrdersJob>(jobBuilder =>
jobBuilder
.WithIdentity(jobKey)
.WithDescription("Processes pending orders"))
.AddTrigger(triggerBuilder =>
triggerBuilder
.ForJob(jobKey)
.WithIdentity($"{nameof(ProcessPendingOrdersJob)}-trigger")
.WithSimpleSchedule(schedule =>
schedule
.WithIntervalInMinutes(5)
.RepeatForever())
.StartNow());
}
}
```
---
## Template: Cron Scheduled Job
```csharp
// src/{name}.infrastructure/BackgroundJobs/DailyReportJob.cs
using Microsoft.Extensions.Logging;
using Quartz;
namespace {name}.infrastructure.backgroundjobs;
/// <summary>
/// Generates daily reports at 6:00 AM every day
/// </summary>
[DisallowConcurrentExecution]
public sealed class DailyReportJob : IJob
{
private readonly IReportService _reportService;
private readonly IEmailService _emailService;
private readonly ILogger<DailyReportJob> _logger;
public DailyReportJob(
IReportService reportService,
IEmailService emailService,
ILogger<DailyReportJob> logger)
{
_reportService = reportService;
_emailService = emailService;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Starting daily report generation...");
var reportDate = DateTime.UtcNow.Date.AddDays(-1);
var report = await _reportService.GenerateDailyReportAsync(
reportDate,
context.CancellationToken);
await _emailService.SendReportAsync(
report,
context.CancellationToken);
_logger.LogInformation(
"Daily report for {Date} sent successfully",
reportDate.ToShortDateString());
}
}
```
```csharp
// src/{name}.infrastructure/BackgroundJobs/DailyReportJobSetup.cs
using Microsoft.Extensions.Options;
using Quartz;
namespace {name}.infrastructure.backgroundjobs;
internal sealed class DailyReportJobSetup : IConfigureOptions<QuartzOptions>
{
public void Configure(QuartzOptions options)
{
var jobKey = JobKey.Create(nameof(DailyReportJob));
options
.AddJob<DailyReportJob>(jobBuilder =>
jobBuilderScaffolds a complete .NET solution following Clean Architecture principles with proper layer separation (API, Application, Domain, Infrastructure). Creates project structure, dependency injection setup, and cross-cutting concerns configuration.
Generates CQRS Commands with Handlers, Validators, and Request DTOs following Clean Architecture patterns. Commands represent actions that modify state and return Result types for proper error handling.
Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.
Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.
Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.
Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.
Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.
Generates Minimal API endpoints following Microsoft's recommended approach. Creates fast, testable HTTP APIs with minimal code using MapGet/MapPost/MapPut/MapDelete. Preferred over controller-based APIs for new projects.