IConfiguration vs IOptions NET
Synchronous and Asynchronous in .NET Core
Model Binding and Validation in ASP.NET Core
ControllerBase vs Controller in ASP.NET Core
ConfigureServices and Configure methods
IHostedService interface in .NET Core
ASP.NET Core request processing
| logging and monitoring in a .NET | Agile-and-QA-Metrics | |
In .NET Core, dependency injection (DI) service lifetimes |
In .NET Core, dependency injection (DI) service lifetimes—transient, scoped, and singleton—determine how instances of a service are created, shared, and managed. They are configured during application startup, typically in Program.cs, using methods like AddTransient(), AddScoped(), and AddSingleton().
Creation: A new instance is created every single time the service is requested from the DI container.
Sharing: No instances are shared. Even within the same HTTP request, if a transient service is injected into multiple components, each will receive its own unique instance.
Best for: Lightweight, stateless services that should not share state. Examples include helper classes, random number generators, and simple utility services.
Registration:
services.AddTransient<IMyTransientService, MyTransientService>();
Creation: A new instance is created once per client request (or "scope"). In a typical ASP.NET Core web application, this means one instance is created per HTTP request.
Sharing: The same instance is shared across all components that request it within the same request scope. A new request, however, will create a new instance.
Best for: Services that need to maintain state throughout a single, contained operation, such as a database context (DbContext) in Entity Framework Core. This ensures that all components within a single request use the same database transaction.
Registration:
services.AddScoped<IMyScopedService, MyScopedService>();
Creation: Only one instance is created for the entire application's lifetime. This happens the first time the service is requested.
Sharing: The exact same instance is shared across all requests and all components in the application until the application is shut down.
Best for: Stateless and thread-safe services that are expensive to create, such as a logging service, a configuration manager, or an in-memory cache. Because the instance is shared, it must be designed to be thread-safe.
Registration:
services.AddSingleton<IMySingletonService, MySingletonService>();
You should never inject a service with a shorter lifetime (scoped or transient) into a service with a longer lifetime (singleton). Doing so can lead to a "captive dependency" bug, where the longer-lived service holds onto an instance of the shorter-lived service for its entire lifetime, overriding its intended behavior. For example, injecting a scoped DbContext into a singleton background service would cause the DbContext instance to live forever, leading to memory leaks and incorrect state.
If you must use a shorter-lived service inside a longer-lived one, you can manually create a service scope. This is often done by injecting IServiceScopeFactory into the singleton service and then creating a new scope to resolve the shorter-lived dependency only when you need it.
Program.csIn .NET 6 and later, service registrations happen directly in Program.cs.
var builder = WebApplication.CreateBuilder(args); // Register services builder.Services.AddTransient\(); builder.Services.AddScoped\ (); builder.Services.AddSingleton\ ();
Imagine you’re at a coffee shop:
Every time you order a coffee, you get a brand new cup.
You get one cup per visit; you can refill it during your stay, but next time you visit, it’s a new cup.
The shop gives you one thermos for life; you carry it everywhere, and it’s refilled forever.
DbContext).In multi-tenant SaaS, be careful with singletons holding tenant-specific data — they can leak state between tenants.
| logging and monitoring in a .NET | Agile-and-QA-Metrics | |