dependency-injection-lifetimeslisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Dependency Injection Lifetimes
## The one rule that causes 90% of DI bugs
A service must not depend on anything with a SHORTER lifetime. Singleton -> Scoped is the captive dependency: the singleton captures the first scope's instance forever.
```csharp
// WRONG: singleton captures a scoped DbContext
services.AddSingleton<ICacheWarmer, CacheWarmer>(); // ctor takes AppDbContext
```
Symptoms in production: `ObjectDisposedException: Cannot access a disposed context`, cross-request data bleed, "second request returns stale data". The default container validates this only when `ValidateScopes` is on - which is Development-only by default. Turn it on everywhere; the check is cheap:
```csharp
builder.Host.UseDefaultServiceProvider(o => { o.ValidateScopes = true; o.ValidateOnBuild = true; });
```
`ValidateOnBuild` also catches missing registrations at startup instead of first-request.
## Consuming scoped services from singletons (the right way)
Background services and singletons that need scoped services create a scope per unit of work:
```csharp
public class OutboxProcessor(IServiceScopeFactory scopeFactory) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// one batch = one scope = one DbContext
}
}
}