Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteiTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Choose a dependency-injection lifetime by asking who should share an instance, how long it should live, and who should dispose it. In Microsoft’s built-in .NET dependency injection, transient means a new instance per resolution, scoped means one instance per scope, and singleton means one instance for the service-provider lifetime. A lifetime mismatch can make request-specific state persist beyond its intended boundary—the familiar kind of bug caused by shared state or an object outliving its owner, though not every such bug has the same cause.
What do transient, scoped, and singleton mean?
A DI container uses a lifetime registration to decide when to create an instance and when to reuse or dispose of it. The table describes Microsoft’s built-in .NET container; other containers and frameworks can define scope behavior differently.
| Lifetime | Instance reuse boundary | State and data fit | Disposal boundary | Thread-safety expectation |
|---|---|---|---|---|
| Transient | A new instance each time the container resolves the service. | Suitable when each resolution should have a separate instance. But if a longer-lived object retains a transient dependency, that instance lasts as long as its owner. | If the container creates a disposable transient, it tracks and disposes it with the scope or provider that resolved it. A transient resolved from the root may therefore remain tracked until provider shutdown. | No automatic guarantee. A transient used by multiple callers or retained by a singleton may need to be thread-safe. |
| Scoped | One instance per scope, reused for resolutions within that scope. | Fits data or work that should be shared inside one operation but not across operations. In ASP.NET Core request processing, a request commonly defines the scope. | Disposed when its scope ends. In a web request, this is normally the end of request processing. | Usually used within its scope, but thread safety still matters if multiple tasks use it concurrently. |
| Singleton | One instance shared by requests from the service provider for that provider’s lifetime. | Fits data and services intentionally shared for the application lifetime. Mutable request-specific state does not belong here. | Container-created singleton instances are disposed when the root provider is disposed. | Must be thread-safe because callers can share the same instance. |
These definitions and the interaction warnings are documented by Microsoft Learn’s service-lifetime guidance. Lifetime is not a simple performance ranking: correctness, retained state, disposal, and object construction all matter.
Recommended Free Tools
Why can a scoped service act like a singleton?
A singleton captures a scoped dependency
A scoped service is designed to end with its scope. If a singleton receives that service through constructor injection, the singleton retains it for much longer. In a web app, that can allow request-specific state to leak across requests or keep a scoped resource alive beyond the request for which it was created. Microsoft calls this a captive dependency: a longer-lived service holding a shorter-lived one.
#1 Best Overall
Microsoft’s guidance is explicit: “Do not resolve a scoped service directly from a singleton using constructor injection or by requesting it from IServiceProvider in the singleton.” See Service lifetimes and Dependency injection guidelines.
A scoped service is resolved from the root provider
Resolving a scoped service directly from the root provider removes the ordinary scope boundary: the instance can effectively behave like a singleton and remain tracked until root-provider shutdown. Resolve scoped services from an active scope instead.
Rank #2
A transient dependency is retained by a long-lived owner
Transient describes how the container creates an instance, not a promise that the object will be short-lived. If a singleton stores a transient in a field, that particular instance remains reachable for the singleton’s lifetime. It may also be accessed concurrently, so assess its state and thread safety based on how it is actually used.
How should you choose a lifetime?
- Start with ownership. Decide which operation or component owns the work and when that work ends. In .NET, a request scope is common in ASP.NET Core, but background work outside a request may need an explicit scope.
- Choose the narrowest reuse boundary that matches the design. Use scoped when one instance should serve a scope; transient when each container resolution should create a separate instance; and singleton only when sharing across the provider’s lifetime is intentional.
- Trace dependencies from long-lived services. A singleton should not retain a scoped service. A transient injected into a singleton is retained too, even though it was registered transient.
- Check state and concurrency. Ask whether callers can see or change the same instance at once. Singletons must be thread-safe; other lifetimes are not automatically safe if instances are shared or used concurrently.
- Check disposal ownership. Let the container dispose container-created dependencies at their appropriate scope or provider boundary. Do not manually dispose a dependency the container owns.
For example, .NET’s AddDbContext registers DbContext as scoped by default. That aligns it with a request scope in common ASP.NET Core applications rather than retaining one context in a singleton. See Microsoft’s lifetime documentation.
Rank #3
How can a singleton or background service perform scoped work?
Create a scope for the operation, resolve the scoped dependency from that scope, finish the work, and let the scope dispose its contents. Do not store the scoped dependency on the singleton for reuse after the scope ends.
- Inject
IServiceScopeFactoryinto the singleton or hosted service. - For each operation, create a scope with
CreateScope(). - Resolve the scoped service from that scope’s
ServiceProvider. - Use it only within the operation, then dispose the scope, typically with
using.
ASP.NET Core conventional middleware has a related lifetime boundary: it is long-lived, so a scoped dependency should be supplied to Invoke or InvokeAsync, or used with factory-based middleware, rather than captured by the conventional middleware constructor. See Dependency injection in ASP.NET Core.
Rank #4
How do you catch lifetime mistakes?
Enable scope validation in development and run the application paths that resolve your services. The built-in .NET provider can detect common cases, including a scoped service being resolved from the root provider or injected into a singleton. Validation is a useful guard, not a replacement for checking ownership and retained references; a transient captured by a singleton, for example, is not itself a scoped-service violation.
When a lifetime problem is suspected, inspect the dependency graph and ask three questions: which provider or scope created the instance, which object still references it, and when does that owner dispose it? Those questions distinguish an incorrect registration from an object that is simply being retained by a longer-lived consumer.
What does “the same bug you already know” mean?
It is an analogy, not a claim that every DI lifetime bug is identical. The underlying pattern is familiar: shared mutable state, request data cached in a global object, or a resource used beyond the boundary that was supposed to own it. A lifetime choice sets that sharing and ownership boundary. When the boundary is too broad, state can contaminate later operations, resources can outlive their intended scope, and memory can remain occupied longer than expected.
These details describe Microsoft.Extensions.DependencyInjection and ASP.NET Core. For another .NET container or a framework such as Spring or Guice, verify that framework’s own scope, resolution, and disposal rules rather than assuming the same defaults. For a broader treatment of DI patterns in C# and .NET, see Manning’s Dependency Injection Principles, Practices, and Patterns.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →

