Transactional enqueue
Enqueue a job on your DbContext, next to your data. If your transaction rolls back, the job never existed.
XactJobs saves a job in the same EF Core transaction as your business data: if it commits, a worker runs the job, and if it rolls back, the job never existed.
dotnet add package XactJobs
Getting started
Tables in your database, two lines at startup, and jobs next to your data.
Run the install script for your database, from sql/.
sqlcmd -I -d <db> -i xact-jobs-sqlserver.sqlpsql -d <db> -f xact-jobs-postgresql.sqlmysql <db> < xact-jobs-mysql.sqlsqlplus <user>@<db> @xact-jobs-oracle.sqlsqlite3 app.db < xact-jobs-sqlite.sqlOr include them in your EF migrations, with ApplyXactJobsConfigurations(..., excludeFromMigrations: false).
Add the entity configurations to your DbContext, and register the workers.
// UserDbContext.OnModelCreating
modelBuilder
.ApplyXactJobsConfigurations(
Database.ProviderName);
// Program.cs: 2 workers
builder.Services
.AddXactJobs<UserDbContext>();Enqueue on your DbContext and save. The job runs when your transaction commits.
db.Orders.Add(order);
db.JobEnqueue<Mailer>(x =>
x.SendConfirmation(order.Number));
// one transaction: order and job
await db.SaveChangesAsync();Features
Everything runs in your app and your database, with the transactions you already have.
Enqueue a job on your DbContext, next to your data. If your transaction rolls back, the job never existed.
A job is a unit of work: its database changes are saved together with its completion, exactly once.
A failed job runs again, waiting longer each time. A job that can never succeed stops itself, without retries.
Run a job after another job, or after a whole batch: when it succeeds, fails or finishes.
Recurring jobs on cron schedules, defined in code, with any number of nodes running your app.
Priority, long-running and your own queues, each with its own workers. QuickPoll starts a job right away.
See the pending, finished and periodic jobs and the batches. Run, retry, pause and trigger them.
SQL Server, PostgreSQL, MySQL, Oracle and SQLite, on .NET 8, 9 and 10. The jobs live in your database, the workers in your app.
Dashboard
The XactJobs.UI package adds a dashboard and a REST API to your app.












The dashboard: due, running, succeeded and failed jobs, and the jobs processed per hour
Measured
2,300–2,600 no-op jobs per second
Draining 20,000 jobs on a Hetzner Cloud CX33 (4 shared vCPUs, 8 GB) with PostgreSQL 16, each job in its own transaction.
The benchmark, to run on your machineThe core is tested on all five databases, with an integration test suite running against each one (SQLite in-process, the others in containers): transactional enqueue, claiming and leasing, retries, batches and continuations, and periodic jobs.
Feedback and issue reports are very welcome, especially from production use. XactJobs was started in 2025, and written without AI. Newer releases are developed with AI, and the changes are reviewed by a human.
Licensed under the MIT License.