Tldr

Transient objects are always different; a new instance is provided to every controller and every service. Scoped objects are the same within a request, but different across different requests. Singleton objects are the same for every object and every request.

Memory trick

T = Tiny/stateless, T t = new(); S = Scope/request, passing a particular object from methodA to methodB S = Shared/application, global


builder.Services.AddTransient<ITransientService, OperationService>();
builder.Services.AddScoped<IScopedService, OperationService>();
builder.Services.AddSingleton<ISingletonService, OperationService>();

1. Transient Lifetime (AddTransient)

  • Behavior:

    • A brand-new instance is created every single time it is requested.
  • Real-World Scenario:

    • A stateless utility or calculation engine, such as a password hasher or an automated text formatting parser.
  • Why Use It:

    • Prevents state leakage. Because the service holds no internal state, giving every consumer a fresh instance completely eliminates multi-threading and concurrency bugs.

Use For Lightweight, stateless services that perform short-lived operations and do not need to share data with other components during an operation.

Eg : calculators, formatters, validators, mappers, and lightweight transformation services.

2. Scoped Lifetime (AddScoped)

  • Behavior:

    • A single instance is created once per client request (one instance per incoming HTTP request in web apps).
  • Real-World Scenario:

    • Entity Framework Core DbContext interacting with a repository pattern.
  • Why Use It:

    • Ensures that everything handling a single HTTP request shares the same Unit of Work without risking cross-request data corruption or tracking conflicts.

Where to Use

Web API controllers, unit-of-work classes, repositories, and business logic layers that interact with databases or maintain state for the duration of a single user transaction.

3. Singleton Lifetime (AddSingleton)

  • Behavior:

    • A single instance is created the first time it is requested and persists across the entire application lifecycle.
  • Real-World Scenario:

    • An in-memory caching service (IMemoryCache) or an application-wide configuration manager.
  • Why Use It:

    • Maximizes performance and reduces memory overhead by preventing repetitive object creation for data that rarely changes and is thread-safe.

Where to Use

Background worker services (IHostedService), caching layers, configuration options, and connection managers shared globally across all users and requests.


Watch out for Captive Dependencies Never inject a Transient or Scoped service directly into a Singleton service. Because the Singleton lives forever, it will "capture" the short-lived dependency, causing it to behave like a Singleton and potentially causing memory leaks or multi-threading bugs.

The rules to remember

DbContext                 → Scoped
 
Request-specific state    → Scoped
 
Stateless lightweight     → Transient
 
Shared immutable state    → Singleton
 
Mutable Singleton         → Check thread safety
 
Singleton → Scoped        → ❌ Avoid
 
ECS Singleton             → Per task/process
 
Lambda Singleton          → Per execution environment