What is the AddSingleton vs AddScoped vs Add Transient C# Asp.net Core?

Dependency injection in ASP.NET Core provides three service lifetime options that determine when service instances are created and disposed. These service lifetimes − AddSingleton, AddScoped, and AddTransient − control the lifecycle of your registered services.

Understanding these lifetimes is crucial for proper memory management and ensuring your application behaves correctly across different requests and users.

Syntax

All three methods follow the same registration syntax in the ConfigureServices method −

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton<IInterface, Implementation>();
    services.AddScoped<IInterface, Implementation>();
    services.AddTransient<IInterface, Implementation>();
}

Service Lifetimes in ASP.NET Core Singleton One instance for entire app Same Lives until app shutdown Scoped One instance per request New Lives until request ends Transient New instance every time Disposed quickly after use

AddSingleton

When you register a service as Singleton, only one instance exists throughout the entire application lifetime. This single instance is shared across all requests and users.

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton<ILog, Logger>();
}

Example

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;

public interface ICounterService {
    int GetNextNumber();
}

public class CounterService : ICounterService {
    private int _counter = 0;
    
    public int GetNextNumber() {
        return ++_counter;
    }
}

public class Program {
    public static void Main(string[] args) {
        var services = new ServiceCollection();
        services.AddSingleton<ICounterService, CounterService>();
        
        var serviceProvider = services.BuildServiceProvider();
        
        // Simulate multiple requests
        var service1 = serviceProvider.GetService<ICounterService>();
        var service2 = serviceProvider.GetService<ICounterService>();
        
        Console.WriteLine($"Service1: {service1.GetNextNumber()}");
        Console.WriteLine($"Service2: {service2.GetNextNumber()}");
        Console.WriteLine($"Same instance: {object.ReferenceEquals(service1, service2)}");
    }
}

The output of the above code is −

Service1: 1
Service2: 2
Same instance: True

AddScoped

When you register a service as Scoped, one instance is created per request. Each HTTP request gets its own instance, but within that request, the same instance is reused.

public void ConfigureServices(IServiceCollection services) {
    services.AddScoped<ILog, Logger>();
}

Example

using Microsoft.Extensions.DependencyInjection;
using System;

public interface IRequestService {
    string GetRequestId();
}

public class RequestService : IRequestService {
    private readonly string _requestId;
    
    public RequestService() {
        _requestId = Guid.NewGuid().ToString("N")[..8];
    }
    
    public string GetRequestId() {
        return _requestId;
    }
}

public class Program {
    public static void Main(string[] args) {
        var services = new ServiceCollection();
        services.AddScoped<IRequestService, RequestService>();
        
        var serviceProvider = services.BuildServiceProvider();
        
        // Simulate first request scope
        using (var scope1 = serviceProvider.CreateScope()) {
            var service1a = scope1.ServiceProvider.GetService<IRequestService>();
            var service1b = scope1.ServiceProvider.GetService<IRequestService>();
            
            Console.WriteLine($"Scope1 - Service1a: {service1a.GetRequestId()}");
            Console.WriteLine($"Scope1 - Service1b: {service1b.GetRequestId()}");
            Console.WriteLine($"Same in scope1: {service1a.GetRequestId() == service1b.GetRequestId()}");
        }
        
        // Simulate second request scope
        using (var scope2 = serviceProvider.CreateScope()) {
            var service2 = scope2.ServiceProvider.GetService<IRequestService>();
            Console.WriteLine($"Scope2 - Service2: {service2.GetRequestId()}");
        }
    }
}

The output of the above code is −

Scope1 - Service1a: a1b2c3d4
Scope1 - Service1b: a1b2c3d4
Same in scope1: True
Scope2 - Service2: e5f6g7h8

AddTransient

When you register a service as Transient, a new instance is created every time the service is requested, even within the same request.

public void ConfigureServices(IServiceCollection services) {
    services.AddTransient<ILog, Logger>();
}

Example

using Microsoft.Extensions.DependencyInjection;
using System;

public interface ITransientService {
    string GetInstanceId();
}

public class TransientService : ITransientService {
    private readonly string _instanceId;
    
    public TransientService() {
        _instanceId = Guid.NewGuid().ToString("N")[..8];
    }
    
    public string GetInstanceId() {
        return _instanceId;
    }
}

public class Program {
    public static void Main(string[] args) {
        var services = new ServiceCollection();
        services.AddTransient<ITransientService, TransientService>();
        
        var serviceProvider = services.BuildServiceProvider();
        
        var service1 = serviceProvider.GetService<ITransientService>();
        var service2 = serviceProvider.GetService<ITransientService>();
        var service3 = serviceProvider.GetService<ITransientService>();
        
        Console.WriteLine($"Service1: {service1.GetInstanceId()}");
        Console.WriteLine($"Service2: {service2.GetInstanceId()}");
        Console.WriteLine($"Service3: {service3.GetInstanceId()}");
        Console.WriteLine($"All different: {service1.GetInstanceId() != service2.GetInstanceId()}");
    }
}

The output of the above code is −

Service1: x1y2z3w4
Service2: a5b6c7d8
Service3: m9n0p1q2
All different: True

Comparison

Lifetime Instance Creation Disposal Best Used For
Singleton Same instance for entire application Application shutdown Stateless services, configuration, caching
Scoped One instance per request End of request Database contexts, user-specific data
Transient New instance every time requested Immediately after use Lightweight, stateless operations

Conclusion

Choose AddSingleton for services that maintain state across the application, AddScoped for services that should be consistent within a request, and AddTransient for lightweight services that don't hold state. Understanding these lifetimes ensures proper memory management and application behavior.

Updated on: 2026-03-17T07:04:36+05:30

11K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements