Skip to content

Feature/Add Quartz.NET Integration for Background Job Scheduling#1263

Open
alnuaimicoder wants to merge 12 commits into
CommunityToolkit:mainfrom
alnuaimicoder:feature/add-quartz-integration
Open

Feature/Add Quartz.NET Integration for Background Job Scheduling#1263
alnuaimicoder wants to merge 12 commits into
CommunityToolkit:mainfrom
alnuaimicoder:feature/add-quartz-integration

Conversation

@alnuaimicoder
Copy link
Copy Markdown

@alnuaimicoder alnuaimicoder commented Apr 2, 2026

Closes #1259

Overview

This PR adds a production-ready integration for background job scheduling using Quartz.NET in .NET Aspire applications.

.NET Aspire currently lacks native support for background job scheduling. Developers must manually integrate Quartz.NET or Hangfire, configure persistence, set up observability, and implement idempotency themselves. This integration solves that problem by providing an Aspire-native solution.

What's Included

Three Packages

  1. CommunityToolkit.Aspire.Hosting.Quartz (Hosting Integration)

    • Resource pattern for Quartz.NET
    • Automatic database configuration (PostgreSQL, SQL Server)
    • Health checks integration
    • OpenTelemetry metrics
    • Automatic schema migrations
  2. CommunityToolkit.Aspire.Quartz (Client Integration)

    • IBackgroundJobClient for dynamic job scheduling
    • Idempotency support (prevent duplicate execution)
    • Retry policies with exponential/linear backoff
    • OpenTelemetry distributed tracing
    • Full Quartz.NET power (cron expressions, triggers, listeners)
  3. CommunityToolkit.Aspire.Quartz.Abstractions (Core Abstractions)

    • Core interfaces: IJob, IBackgroundJobClient
    • Shared types: JobOptions, RetryPolicy, JobContext

Key Features

  • Aspire-native patterns - Built for Aspire from the ground up
  • Production features - Idempotency, OpenTelemetry, health checks out of the box
  • Multi-database support - PostgreSQL and SQL Server with auto-migrations
  • Full Quartz.NET power - No feature hiding, complete access to Quartz.NET APIs
  • All-in-one architecture - No separate worker needed, jobs run in your API service
  • Type-safe API - Strong typing with generics for job parameters

Usage Example

AppHost

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("postgres")
    .AddDatabase("quartzdb");

builder.AddProject<Projects.ApiService>("api")
    .WithReference(postgres);

builder.Build().Run();

API Service

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.Services.AddQuartzClient(builder.Configuration.GetConnectionString("quartzdb"));

var app = builder.Build();

app.MapPost("/jobs/enqueue", async (IBackgroundJobClient jobClient) =>
{
    var jobId = await jobClient.EnqueueAsync<SendEmailJob>(
        new { email = "user@example.com" },
        new JobOptions { IdempotencyKey = "email-123" });

    return Results.Ok(new { jobId });
});

app.Run();

Job Definition

public class SendEmailJob : IJob
{
    private readonly ILogger<SendEmailJob> _logger;

    public SendEmailJob(ILogger<SendEmailJob> logger) => _logger = logger;

    public async Task Execute(IJobExecutionContext context)
    {
        var email = context.JobDetail.JobDataMap.GetString("email");
        _logger.LogInformation("Sending email to {Email}", email);
        await Task.Delay(1000);
        _logger.LogInformation("Email sent successfully!");
    }
}

Proof of Concept

This integration has been published and tested:

PR Checklist

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • New integration
  • Docs are written (README.md in each package)
  • Added description of major feature to project description for NuGet package
  • Tests for the changes have been added
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs
  • Code follows all style conventions

What's Completed

✅ Core Implementation

  • Three packages (Hosting, Client, Abstractions)
  • Multi-targeting (.NET 8.0, 9.0, 10.0)
  • All source code with proper namespaces
  • XML documentation on all public APIs
  • Package metadata (Description + AdditionalPackageTags)

✅ Testing

  • 10 Unit Tests - All passing
    • 6 client tests (JobOptions, RetryPolicy, JobContext, BackoffStrategy)
    • 4 hosting tests (Resource creation, connection strings, multiple resources)
  • Test projects added to CI/CD workflow (tests.yaml)
  • Tests will run automatically on all PRs

✅ Documentation

  • Package README files (all 3 packages)
  • Main README.md updated with integration table entry
  • Shield badges configured (stable + preview)
  • Database migration documentation

✅ Example Application

  • AppHost - Aspire orchestration with PostgreSQL
  • ApiService - Background job scheduling with SignalR
  • Web - Blazor dashboard for monitoring jobs
  • ServiceDefaults - Shared configuration
  • All projects build successfully

✅ Package Management

  • All package versions in Directory.Packages.props
  • Central Package Management configured
  • Quartz 3.16.1, Npgsql 10.0.2, etc.

✅ API Tracking

  • PublicAPI.Shipped.txt files created
  • PublicAPI.Unshipped.txt files populated
  • All public APIs tracked

✅ CI/CD Integration

  • Added to .github/workflows/tests.yaml
  • Both test projects in matrix
  • Will run on Ubuntu and Windows

✅ Solution Integration

  • All 3 source projects in CommunityToolkit.Aspire.slnx
  • All 2 test projects in solution
  • All 4 example projects in solution

Testing Coverage

Unit Tests (10 tests - All Passing ✅)

Client Tests (6 tests)

  • JobOptions validation and configuration
  • RetryPolicy with exponential/linear backoff
  • JobContext data handling
  • BackoffStrategy calculations

Hosting Tests (4 tests)

  • QuartzResource creation
  • Connection string configuration
  • Multiple resource instances
  • Resource annotations

Integration Tests (Optional - Can be added later)

  • End-to-end job execution with PostgreSQL
  • Database migration verification
  • Health check validation
  • OpenTelemetry trace verification

Other Information

Why This Integration Matters

Background job scheduling is a fundamental requirement for most production applications. Currently, .NET Aspire developers must:

  1. Manually integrate Quartz.NET or Hangfire
  2. Configure database persistence themselves
  3. Set up observability from scratch
  4. Implement idempotency and retry logic
  5. Handle health checks separately

This integration makes background jobs a first-class citizen in Aspire, just like databases, caches, and messaging.

Design Decisions

  1. Three-package structure: Separates abstractions, client, and hosting for clean dependency management
  2. Aspire.Hosting namespace: Extension methods in Aspire.Hosting for discoverability
  3. No wrapper abstraction: Full access to Quartz.NET APIs - we enhance, not hide
  4. All-in-one architecture: Jobs run in API service, no separate worker needed (simpler deployment)
  5. Multi-database support: PostgreSQL and SQL Server (MySQL and SQLite can be added later)

Maintenance Commitment

I'm committed to:

  • Long-term maintenance of this integration
  • Responding promptly to issues and feedback
  • Keeping up with Aspire and Quartz.NET updates
  • Adding features based on community needs

Database Migration

The integration includes automatic database migration:

  • On application startup, QuartzMigrationService checks if Quartz tables exist
  • If not, executes SQL script to create all required tables
  • Tables are stored in the configured database (PostgreSQL or SQL Server)
  • No manual migration files needed

Production Features

  • Idempotency: Prevent duplicate job execution using IdempotencyKey
  • OpenTelemetry: Full distributed tracing and metrics
  • Health Checks: Built-in health check support
  • Retry Policies: Exponential and linear backoff strategies
  • Type Safety: Strong typing with generics

Branch: feature/add-quartz-integration
Original Repository: https://github.com/alnuaimicoder/aspire-hosting-quartz
Status: ✅ Ready for Review - All requirements met

- Add CommunityToolkit.Aspire.Hosting.Quartz (hosting integration)
- Add CommunityToolkit.Aspire.Quartz (client integration)
- Add CommunityToolkit.Aspire.Quartz.Abstractions (core abstractions)
- Rename csproj files to match CommunityToolkit naming
- Update package metadata

Still TODO:
- Update namespaces in C# files
- Add unit and integration tests
- Create example application
- Add PublicAPI.txt files
- Update documentation
- Remove custom metadata (handled by Directory.Build.props)
- Add Description and AdditionalPackageTags
- Update project references to use CommunityToolkit naming
- Add 'client' and 'hosting' tags as required
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 2, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1263

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1263"

- Add api/ folders to all three projects
- Add PublicAPI.Shipped.txt (empty for new integration)
- Add PublicAPI.Unshipped.txt with all public APIs
- Follows CommunityToolkit API tracking conventions
- Update package names in all README files
- Update installation instructions
- Point to aspire.dev for documentation
- Remove references to original repository
- Mark completed tasks
- Update remaining tasks list
- Add time estimates
- Clarify next steps
- Add QuartzResourceTests for hosting integration
- Add QuartzClientTests for client integration
- Test resource creation and configuration
- Test service registration
- Test JobOptions and RetryPolicy
- All tests follow xUnit conventions
- Added Quartz, Quartz.Extensions.Hosting, Quartz.Serialization.Json packages (v3.16.1)
- Added Npgsql (v10.0.2) and Microsoft.Data.SqlClient (v6.1.4)
- Created unit tests for hosting and client components
- Added XML documentation comments for all public APIs
- Fixed test implementations to match actual API signatures
- All tests passing (10 total: 4 hosting + 6 client)
- Added SignalR Client (v10.0.5) and Npgsql.EntityFrameworkCore.PostgreSQL (v10.0.1)
- Started Quartz example structure in examples/quartz/
- Fixed ServiceDefaults to use correct OpenTelemetry API
- Example needs namespace cleanup (in progress)
- Fixed namespace conflict in QuartzJobScheduler.cs using global:: qualifier
- Removed OpenApi calls (AddOpenApi, MapOpenApi) - not needed
- Removed EF Core migration code - using built-in QuartzMigrationService
- Deleted EF Core Data folder and migrations
- Updated README files to clarify automatic database migration
- All 10 unit tests passing
- Example application builds successfully
- Added three Quartz packages to integrations table
- Added shield badges and documentation links
- Positioned alphabetically between Python and KurrentDB
- Added Hosting.Quartz.Tests to integration tests matrix
- Added Quartz.Tests to client tests matrix
- Tests will run automatically on PRs and main branch
- Added 4 example projects in /examples/quartz/ folder
- Added 3 source libraries in /src/ folder
- Added 2 test projects in /tests/ folder
- All projects alphabetically ordered
@alnuaimicoder
Copy link
Copy Markdown
Author

@dotnet-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add AspireQuartz - Production-Ready Background Job Scheduling for .NET Aspire

2 participants