OrdersController's constructor asks for an IOrderService, and one just appears — that's the built-in DI container, configured in Program.cs:
builder.Services.AddScoped<IOrderService, OrderService>();
Three lifetimes decide how long an instance is reused:
| Lifetime | New instance created | Typical use |
|---|---|---|
Transient | Every time it's requested | Lightweight, stateless services |
Scoped | Once per HTTP request | Anything using a DbContext — one unit of work per request |
Singleton | Once for the whole app's lifetime | Shared, thread-safe state (e.g. an in-memory cache, configuration) |
Getting this wrong in one specific way causes a real, hard-to-spot bug: injecting a Scoped service into a Singleton (a "captive dependency"). The Singleton is only constructed once, so whatever Scoped instance is injected into it at that moment gets captured and reused forever — silently breaking the per-request isolation the Scoped lifetime was supposed to guarantee, and (for a DbContext specifically) leading to a shared, long-lived database context used concurrently across unrelated requests, which is not thread-safe and produces confusing, intermittent errors.
// Wrong — CacheService is a singleton, so the OrderRepository it captures at construction
// time is frozen forever, defeating its per-request Scoped lifetime.
builder.Services.AddSingleton<CacheService>();
builder.Services.AddScoped<OrderRepository>();
class CacheService
{
public CacheService(OrderRepository repository) { /* captured once, reused forever */ }
}
The framework can catch this automatically in Development if ValidateScopes is enabled (it is, by default, in the default templates), throwing at startup instead of producing a silent bug in production — one of the more valuable free safety nets in the framework, worth confirming is actually turned on rather than accidentally disabled.
The fix, when a singleton genuinely needs scoped data, is to inject IServiceScopeFactory (or IServiceProvider) instead of the scoped service directly, and create a new scope explicitly whenever the singleton needs one:
class BackgroundReportService
{
private readonly IServiceScopeFactory _scopeFactory;
public BackgroundReportService(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
public async Task RunReportAsync()
{
using var scope = _scopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<OrderRepository>(); // fresh, correctly-scoped instance
// ...
}
}