What is the difference between Transient, Scoped and Singleton?
Quick Answer
Transient: new instance on every injection — most short-lived. Scoped: one instance per request, shared within that request. Singleton: one instance for the app's entire lifetime, shared by every request and thread. Choose the shortest lifetime that still lets the service do its job, and never inject a shorter-lived service into a longer-lived one.
Transient is a fresh instance every single time it's asked for. Scoped is one instance per request — so within one HTTP call, every class that needs it gets the same object, and then it's thrown away at the end of the request. Singleton is one object for the whole application, created once and shared by every request and every thread, so it has to be thread-safe. My default is scoped for anything touching the database or request state, transient for small stateless services, and singleton only for genuinely app-wide, stateless or safely-shared things like a cache. And the golden rule: a singleton can only depend on other singletons.
| Aspect | Transient | Scoped | Singleton |
|---|---|---|---|
| Instances | One per resolve | One per request/scope | One per app |
| Shared across a request | No | Yes | Yes |
| Shared across requests | No | No | Yes |
| Thread-safety needed | Rarely | No (single request) | Yes |
| Typical use | Small stateless helpers | DbContext, unit of work, request context | Caches, config, clock, HttpClientFactory internals |
| May safely depend on | Transient, Scoped, Singleton | Scoped, Singleton (and Transient) | Singleton only |
Practical notes:
- A transient service resolved by a scoped service effectively lives as long as that scope (it is held by the scoped object).
- A transient
IDisposablecaptured by a singleton is never disposed until app shutdown — a subtle leak. - Registering the same type as scoped in one place and singleton in another leads to confusing behaviour; pick one.
// Two injections in the SAME request:
// Transient -> a1 != a2
// Scoped -> b1 == b2
// Singleton -> c1 == c2
// The SAME registration across TWO requests:
// Transient -> different
// Scoped -> different (scope ended)
// Singleton -> identical
Practise the whole thread the way an interviewer walks it — each question builds on the last.
- What is Dependency Injection in ASP.NET Core?
- What is loose coupling?
- What is Inversion of Control (IoC)?
- What are service lifetimes in ASP.NET Core?
- What is the difference between Transient, Scoped and Singleton?You are here
- Why is DbContext registered as Scoped?
- What is a captive dependency?