Back to results for “Dependency injection”
.NET › Dependency Injection

What is the 'captive dependency' problem, and how does the DI container help catch it?

Advanced

Answer

A captive dependency occurs when a longer-lived service (Singleton) depends on a shorter-lived service (Scoped or Transient), effectively 'capturing' that shorter-lived instance for the Singleton's entire lifetime -- defeating the purpose of the shorter lifetime and potentially causing serious bugs (like a captured DbContext being reused indefinitely across unrelated requests, sharing stale state or causing concurrency issues). ASP.NET Core's DI container detects this specific case (Singleton depending on Scoped) by default and throws an InvalidOperationException at service resolution time, unless scope validation is explicitly disabled.
builder.Services.AddSingleton<INotificationService, NotificationService>();
builder.Services.AddScoped<AppDbContext>();

// NotificationService depending on AppDbContext throws at startup/resolution:
// "Cannot consume scoped service 'AppDbContext' from singleton 'INotificationService'"

// Correct fix: inject IServiceScopeFactory into the Singleton instead
// and create scopes on demand for each unit of work
Real-world example A subtle production bug where all users started seeing the same cached database results was traced to a captive dependency -- a Singleton service had captured a Scoped DbContext, which validation would have caught immediately if it hadn't been running in an environment with scope validation disabled.

Common follow-ups: How do you fix a captive dependency once identified?;Why is scope validation enabled by default in Development but not always in Production?