If you have ever worked with dependency injection in ASP.NET Core, you have most probably come across three key terms when registering services:
Singleton, Scoped, and Transient.
These lifetimes determine how and when your service objects are created and understanding them can save you from annoying bugs and performance bottlenecks.
But First lets Understand DEPENDENCY INJECTION
What is Dependency Injection?
Dependency Injection (DI) is a design pattern where objects get their dependencies (services) from an external source (like a DI container) rather than creating them themselves.
source: here
In .NET Core, we typically register services in the Startup.cs/Program.cs using:
services.AddSingleton<IMyService, MyService>();
services.AddScoped<IMyService, MyService>();
services.AddTransient<IMyService, MyService>();
1) Singleton: As the name suggests One and Only One
A Singleton service is created once and shared across the entire application lifetime. Every request, every user gets the same instance.
Where is it Used?
- Configuration services
- Logging services
- Cache managers
Real-life Example:
In a global weather station, everyone refers to the same source to get the current weather data. No matter who asks, it’s the same answer.
Where to be careful with this?
If it holds state (like user-specific data), it can lead to shared state issues and is not thread-safe by default.
Code:
services.AddSingleton<IWeatherService, WeatherService>();
2) Scoped: One Per Request
A Scoped service is created once per HTTP request. The same instance is reused throughout that request but new for every incoming request.
Where is it Used?
- Database contexts (like DbContext)
- Business logic services that are per-request
Real-life Example:
Imagine a restaurant bill. One bill is generated per table (request), and all orders for that table go on the same bill. Once the guests leave, the bill is thrown away and a new one is started.
Where to be careful with this?
Be careful when injecting scoped services into singletons—it can cause runtime exceptions due to lifetime mismatches.
Code:
services.AddScoped<IOrderService, OrderService>();
3) Transient: New Every Time
A Transient service is created every time it's requested. Even within the same request, asking twice gives you two different instances.
Where is it Used?
- Lightweight, stateless services
- Helper classes like formatters, calculators
Real-life Example:
Ordering coffee at a cafe. Every person (or even the same person) gets a fresh cup made from scratch each time.
Where to be careful with this?
Too many transient services, especially expensive ones, can impact performance.
Code:
services.AddTransient<INotificationService, NotificationService>();
🧩 Conclusion
Understanding service lifetimes is crucial in building scalable and maintainable applications. If you pick the wrong one, you may run into memory leaks, performance issues, or even data corruption.
source: here
Signing Off
Anisha 💗
Top comments (1)
Really good explanation of .NET topics 🫡.
Thanks