ASP.NET Core

Fix the background worker's scope before changing service lifetimes

A scoped-service error needs an ownership fix. Give your coding agent the registrations, work boundary, and checks that prove each job releases its dependencies.

Create a scope for each independent unit of background work, await that work, and dispose the scope before starting the next unit.

Trace the dependency that crosses the boundary

This is an evergreen engineering technique, not a release announcement. Consider a worker that processes queued exports. It starts failing after a scoped export processor is added to its constructor. An assistant proposes changing that processor to a singleton. Before accepting, ask which dependencies the processor owns and how long each should live.

Microsoft documents that AddHostedService registers the hosted service as a singleton, without creating a scope for it. AddDbContext normally registers a scoped context. A worker that captures that dependency chain therefore needs more than a registration edit.

Paste the exception into the free .NET error investigator linked below. It can recognize common lifetime-mismatch messages and prepare a debugging brief. Add the worker constructor, service registrations, and processing method yourself; a stack trace cannot supply those facts. Review redaction before sharing the brief.

Make one job the ownership boundary

For independent exports, our suggested boundary is one scope per export. Inject IServiceScopeFactory into the worker. Inside the job-processing method, create an async scope, resolve the processor from that scope, and await processing before disposal. Do not retain the processor in a worker field or let fire-and-forget work escape.

The following implementation brief is a proposed task for your existing assistant, not compiled or tested application code. It deliberately leaves queue acknowledgment, retries, and scheduling unchanged.

Inspect the registrations and export-processing path first.
Identify every scoped dependency captured by the worker.
For each independent export:
- CreateAsyncScope through the injected IServiceScopeFactory.
- Resolve the scoped processor from scope.ServiceProvider.
- Await processing with the worker cancellation token.
- Use await using so asynchronous disposal completes.
Do not promote scoped services to singletons.
Preserve the existing queue and failure-handling contract.

Keep concurrency separate from scoping

A fresh scope does not make its objects safe to share concurrently. EF Core does not support overlapping operations on one DbContext. Await each database operation before reusing that context; parallel exports need separate contexts. Ask the agent to flag any Task.WhenAll that would share one.

Scope disposal also does not prove an export succeeded or make retries safe. Review acknowledgment and duplicate-processing behavior separately. This change should repair dependency ownership without silently redesigning delivery guarantees.

Require evidence that survives a second job

Keep scope validation enabled in the verification host. Microsoft describes validation that catches scoped dependencies resolved from the root provider or injected into singletons. Passing startup alone is insufficient; exercise the actual processing path.

Ask the agent for these application-level checks, then run them against your target runtime. Record observed results rather than treating generated tests as proof.

  • Process two independent jobs sequentially using a scoped test probe; assert different instances across jobs and disposal after each job.
  • Cancel processing and inject a processing failure; verify disposal still occurs and existing cancellation and failure policies remain intact.
  • If parallel processing is supported, verify each job owns a separate scope and context, with no unawaited database work.

Sources