Skip to content

Pragmatic.Jobs

AOT-safe background job scheduling for .NET 10 — recurring cron jobs, delayed fire-and-forget, continuation chains, and lease-based distributed locking, all source-generated at compile time.

Background jobs in .NET usually mean Hangfire or Quartz.NET. Both rely on runtime reflection for job discovery, serialization, and invocation, require separate dashboards/storage, and keep retry config outside the job definition — so the job class has no idea how it’ll be scheduled, retried, or timed out.

// Without Pragmatic: config separate from the job
RecurringJob.AddOrUpdate<DailyReportJob>("daily-report", x => x.Execute(), "0 2 * * *");
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 3 }); // global, not per-job

Declare scheduling, retry, and timeout on the job class. The generator produces the invoker with an inline retry loop, linked cancellation tokens, telemetry, and DI wiring — zero reflection.

[RecurringJob("0 2 * * *", Id = "daily-report")]
[Retry(MaxAttempts = 3, Strategy = BackoffStrategy.ExponentialWithJitter, BaseDelayMs = 1000)]
[Timeout(TimeoutSeconds = 600)]
public sealed partial class DailyReportJob(IReportService reports) : IJob
{
public async Task ExecuteAsync(JobContext context, CancellationToken ct)
=> await reports.GenerateDailyAsync(context.ScheduledAt.Date, ct);
}

That’s the whole job. The generator emits the invoker (retry + timeout + telemetry), the DI registration, an AOT-safe type registry (no Type.GetType), the recurring definition, and metadata.

Terminal window
dotnet add package Pragmatic.Jobs
dotnet add package Pragmatic.Jobs.EFCore # optional: durable, distributed job store
dotnet add package Pragmatic.SourceGenerator # the unified analyzer
// A delayed, fire-and-forget job
[Job]
[Retry(MaxAttempts = 5)]
public sealed partial class SendWelcomeEmailJob(IEmailService email) : IJob<Guid>
{
public async Task ExecuteAsync(Guid userId, JobContext context, CancellationToken ct)
=> await email.SendWelcomeAsync(userId, ct);
}
// Schedule it
await scheduler.ScheduleAsync<SendWelcomeEmailJob, Guid>(userId, delay: TimeSpan.FromMinutes(5));

Enable processing in the host with app.UseJobs(jobs => jobs.WithWorkerCount(2)). Full walkthrough: Getting Started.

  • [Job] / [RecurringJob(cron)] — fire-and-forget or recurring (cron) jobs.
  • [Retry] / [Timeout] — per-job resilience, inline in the generated invoker.
  • [ContinueWith<T>] — continuation chains.
  • EF Core persistence — durable jobs with lease-based distributed locking (one worker per job across instances).
  • Messaging bridgePragmatic.Messaging.Jobs for scheduled message delivery.

Recurring/delayed jobs, retry/timeout, continuations, EF Core persistence, and distributed locking are functional within the 0.8 preview. See the roadmap.

| Concepts | Job types, scheduler/store model, the invoker pipeline, continuation chains, distributed locking | | Getting Started | Your first recurring and delayed jobs, host wiring | | Common Mistakes | The most frequent job pitfalls | | Troubleshooting | Problem/solution guide with diagnostics |

  • .NET 10.0+
  • Pragmatic.SourceGenerator analyzer

Part of the Pragmatic.Design ecosystem — see Licensing. Pragmatic.Jobs is licensed under the PolyForm Small Business 1.0.0 license (free for small businesses; commercial license above the threshold).