LyraLearn AI Learning Platform
Exams
← Module 3 Β· ASP.NET Core Scenarios
🎧 Listen

DI Lifetime Scenarios

Dependency injection lifetimes are a favorite interview topic, because the definitions are easy and the failure modes are subtle. Expect definitions as a warm-up, then a scenario built around a captive dependency.

The warm-up, answered with intent

"Singleton, scoped, transient β€” go." Don't just define; attach the use case to each: Singleton β€” one instance for the process lifetime; for stateless, thread-safe services (configuration wrappers, HttpClient-backed API clients, caches). Scoped β€” one instance per request; the natural home of DbContext and anything carrying per-request state. Transient β€” new instance every resolution; for lightweight, stateless helpers. The sentence that upgrades the answer: "lifetime is really a statement about state and thread-safety β€” a singleton must be safe for concurrent requests; scoped things must never outlive their request."

The captive dependency scenario

"A background-ish singleton service takes a DbContext in its constructor. It works on the dev box. In production you get intermittent ObjectDisposedException and cross-request data weirdness. What happened?"

This is the captive dependency β€” the classic. Reason it aloud: the singleton is constructed once and holds ("captures") whatever was injected β€” so the scoped DbContext from the first resolution lives forever inside it. Consequences: the context's scope ends and disposal makes later calls throw; worse, DbContext is not thread-safe, and a singleton is hit by concurrent requests, so you can also get corrupted change-tracker state and one user's data bleeding into another's query. It "works" locally because one developer clicking around never overlaps requests.

How to spot it: any longer-lived service depending on a shorter-lived one β€” singleton β†’ scoped is the deadly pair. The default DI container validates scopes in Development (ValidateScopes), which is why the honest verification answer is "this often would have thrown on startup in dev β€” I'd check whether validation was disabled or the resolution went through a root-scope path like a hosted service."

How to fix it: don't inject the scoped thing β€” inject IServiceScopeFactory, create a scope per unit of work, resolve the DbContext inside it, dispose the scope. Six lines:

using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await ProcessBatchAsync(db);

This is exactly the shape of every hosted/background service that touches EF β€” say that, because it connects the trivia to real architecture.

Answers that fall flat in this round

Claiming "make everything transient to be safe" (a per-resolution DbContext breaks unit-of-work semantics and change tracking); asserting singletons are "faster" as the design driver; or fixing the scenario by making DbContext a singleton β€” the one answer worse than the bug.

Practice prompts:

  1. Explain the captive dependency to a junior in 90 seconds using the singleton→DbContext example, including why production surfaces it and dev doesn't.
  2. "Where does a hosted service that processes uploaded transcripts nightly get its DbContext?" Answer with the scope-factory pattern aloud.
  3. Pick three services from your real app and defend the lifetime each one has.
🧠 Quiz yourself on this lesson →

Ask the AI Tutor

Grounded in the course lessons β€” it cites its sources and says when it doesn't know.