The Captive Dependency Pitfall & Scope Validation
1Concept
A Captive Dependency occurs when a Singleton service injects a Scoped service. The Scoped service is captured by the Singleton and never disposed, causing memory leaks and database connection pool exhaustion.
2Architecture Diagram
[ Singleton CacheService ]
│ (Captures and holds forever!)
▼
[ Scoped DbContext ] ──> NEVER Disposed! Concurrency collisions on subsequent HTTP requests!3Code Example
C# 13 & .NET 9
using System;
using Microsoft.Extensions.DependencyInjection;
public class CaptiveDependencyDetection
{
public static void Main()
{
var services = new ServiceCollection();
// Enabling ValidateScopes:
var provider = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = true
});
Console.WriteLine("ValidateScopes: true guarantees runtime exceptions if a Singleton captures a Scoped service.");
}
}4Expected Output
ValidateScopes: true guarantees runtime exceptions if a Singleton captures a Scoped service.
5Key Takeaways
- ✓Always enable `ValidateScopes = true` in development environments.
- ✓If a Singleton requires a Scoped service, inject `IServiceScopeFactory` and create explicit transient scopes.
- ✓Never store Scoped service instances in static or singleton fields.