How to Implement Background Jobs in ASP.NET Core 10 Using Hangfire and SQL Server (2026)
Hangfire stores background jobs in SQL Server so an ASP.NET Core request can return after enqueuing work. A worker later loads the job and executes it. Treat a job as potentially repeatable. If a worker records a result and fails before marking the job complete, another worker can run it again.
This example uses a database receipt keyed by order ID to make that repeat safe. The job does not send email or call a payment system. Those side effects need their own idempotency boundary.
Architecture and prerequisites
In the reference build, explicitly reference System.Security.Cryptography.Pkcs 10.0.14 to avoid vulnerable transitive versions. Keep NuGet auditing enabled and update the pin as patched releases become available.
Install .NET 10 and start a SQL Server instance you control. Create a database called JobsDemo. Create the application table with a migration or this SQL. Hangfire manages its own storage schema separately.
Create a web project and add Hangfire.AspNetCore, Hangfire.SqlServer and Microsoft.Data.SqlClient. Keep the two Hangfire package versions aligned and explicitly reference Newtonsoft.Json 13.0.4 to avoid the vulnerable version resolved by the default dependency graph. Set ConnectionStrings__Jobs through user secrets or protected process configuration. The lab connection needs access to the application table and permission to initialize Hangfire’s schema. Pre-provision the schema and restrict the worker login in production. Keep schema migrations separate from routine job execution.
Hangfire ASP.NET Core setup · SQL Server storage
Implement the job and endpoints
Replace Program.cs in a minimal web project. The entire demo assumes direct loopback access and must never sit behind a reverse proxy. A local address is not a trusted authorization boundary behind a proxy. For deployment, remove the lab guard only after replacing it with authenticated endpoint policies and a custom dashboard authorization filter.
The queue stores a serialized method call and its arguments. Pass stable identifiers instead of large objects, personal records or secrets. The worker should reload the current state as needed. The sample registers a worker in the API process. A separate worker process is useful when job execution and HTTP handling need independent scaling or deployments.
The primary key protects the receipt even when two workers attempt the insert concurrently. The code handles the duplicate constraint as an already-completed operation. Keep that catch scoped to the intended insert. If the table gains other unique constraints, distinguish the expected conflict explicitly.
Verify the behavior
Start the application directly on loopback. POST twice to /jobs/receipts/101. Expect two HTTP 202 responses with different Hangfire job IDs. Open /hangfire locally and follow both jobs. Once both have run, query the table. Expect one row for order 101.
POST to /jobs/reminders/102. The job should appear as scheduled before processing. The two-minute delay is the earliest eligible time. Polling and queue load can make execution later. The daily job uses a fixed demonstration order and a stable recurring-job ID. It deliberately shows how a schedule can repeat while the business receipt remains unique. These are expected results. SQL Server execution was not verified here.
| Job | Trigger | Operational consideration |
|---|---|---|
| Fire-and-forget | Enqueued immediately | A successful response confirms enqueueing. |
| Delayed | Becomes eligible after a delay | Keep a worker running when due. |
| Recurring | Schedule produces a job | Confirm timezone and monitor missed or late executions. |
Retry and monitoring decisions
An exception can trigger a retry. This sample configures three retries for the job. An unrecoverable error should be visible to operators after retries are exhausted. Track queue age, processing time, failed jobs and retry volume. Queue length alone can be misleading if a few expensive jobs occupy all workers.
For a real external notification, use a stable event ID, an outbox record and a provider-side deduplication key where available. Enqueuing a job and separately committing a business change creates a gap between two writes. See the ASP.NET PB transactional outbox guide for the database transaction boundary.
Troubleshooting and FAQs
The dashboard loads but jobs never run. Check that a Hangfire server is registered and appears online. Verify that the API and worker connect to the same database and queues.
Why was my job invoked twice? A retry, duplicate enqueue or worker interruption can cause repeated execution. Deduplicate the business effect rather than relying on the number of invocations.
Can I expose the dashboard to all signed-in users? Restrict it to an operations policy. Job arguments, error details and controls can be sensitive. Require HTTPS and verify authorization at the dashboard itself.
How do I deploy safely while jobs are queued? Keep serialized method signatures compatible with queued work, or drain and migrate it deliberately. Tune worker concurrency according to downstream limits.
A useful release check is to force the same business job twice and inspect the durable result. Queue persistence lets the work resume. The database constraint makes repetition harmless for the specific operation shown here.
Hangfire recurring jobs · Dashboard authorization · Exception handling
Comments
Post a Comment