.NET Dependency Injection Lifetimes Explained

A clear and practical guide to the three main service lifetimes in .NET dependency injection: Transient, Scoped, and Singleton. Understand the difference and when to use each one.

Dependency Injection (DI) is at the heart of the modern ASP.NET Core framework. It's a powerful pattern for building loosely coupled and testable applications. A crucial part of using the built-in DI container effectively is understanding service lifetimes.

When you register a service, you have to tell the container how to manage its lifecycle. Should it create a new instance every time it's requested? Or should it reuse the same instance? This is what lifetimes control. There are three main lifetimes in .NET: Transient, Scoped, and Singleton.

Let's break them down.

1. Transient

  • Lifetime: A new instance is created every time the service is requested.
  • Registration: builder.Services.AddTransient<IMyService, MyService>();

When to use it: Transient is the best choice for lightweight, stateless services. If a service doesn't hold any state (i.e., it has no fields or properties that change during its lifetime) and is cheap to create, Transient is a safe and correct default.

Example Use Case: A simple calculator service that performs calculations but doesn't store any previous results.

public interface ICalculator
{
    int Add(int a, int b);
}

public class Calculator : ICalculator
{
    public int Add(int a, int b) => a + b;
}

If two different services in the same request both ask for an ICalculator, they will each get their own, separate instance.

2. Scoped

  • Lifetime: A single instance is created once per client request (the "scope"). The same instance is then shared across all services that request it within that single request.
  • Registration: builder.Services.AddScoped<IMyService, MyService>();

When to use it: Scoped is the most commonly used lifetime in web applications. It's the perfect choice for services that need to maintain state within a single request, but that state should be isolated between different requests.

Example Use Case: An Entity Framework Core DbContext. You want all the different repositories and services that are involved in a single API request to share the same DbContext instance so they can participate in a single unit of work or transaction. However, you absolutely do not want different requests (e.g., from different users) to share the same DbContext instance.

// In Program.cs
builder.Services.AddDbContext<MyDbContext>(options => ...);
// AddDbContext registers the context with a Scoped lifetime by default.

3. Singleton

  • Lifetime: A single instance is created only once, the first time it is requested. That same instance is then used for the entire lifetime of the application, across all requests.
  • Registration: builder.Services.AddSingleton<IMyService, MyService>();

When to use it: Singleton should be used for services that are expensive to create, are thread-safe, and need to share a global state.

Example Use Case:

  • A caching service (like IMemoryCache) where you want all parts of your application to share the same cache.
  • A configuration object that is loaded once at startup and then read by many different services.
  • A logging service.

Important Warning: Be very careful when injecting a Scoped or Transient service into a Singleton service. This is known as a captive dependency. The Singleton service will hold onto the Scoped/Transient service for its entire lifetime, effectively promoting it to a Singleton, which can lead to unexpected and hard-to-debug bugs. The DI container will warn you about this in development.

Summary Table

Lifetime When is a new instance created? Use Case Example
Transient Every time it is requested. Lightweight, stateless services (e.g., a calculator).
Scoped Once per client request (the scope). Services that share state within a request (e.g., DbContext).
Singleton Only once, for the application's lifetime. Global, shared services (e.g., a caching service).

Conclusion

Choosing the correct service lifetime is a fundamental skill for any .NET developer. By understanding the difference between Transient, Scoped, and Singleton, you can ensure that your application is both correct and efficient. As a rule of thumb:

  • Start with Scoped for most services in a web application.
  • Use Transient for simple, stateless services.
  • Use Singleton with care for services that are explicitly designed to be shared globally and are thread-safe.

Comments

Share your thoughts and insights in the comments below. We'd love to hear your perspective on this topic!

Geek Cafe LogoGeek Cafe

Your trusted partner for cloud architecture, development, and technical solutions. Let's build something amazing together.

Quick Links

© 2025 Geek Cafe LLC. All rights reserved.

Research Triangle Park, North Carolina

Version: 8.9.22