Singleton vs Scoped vs Transient Services in ASP.NET Core

Singleton vs. Scoped vs. Transient Services in ASP.NET Core:

In this article, I will discuss the Singleton vs. Scoped vs. Transient Services in ASP.NET Core Application with Real-Time Examples. Please read our previous article before proceeding to this article, where we discussed ASP.NET Core Dependency Injection with Examples.

Example to Understand Singleton vs. Scoped vs. Transient Services in ASP.NET Core:

Let us understand the differences between Singleton vs. Scoped vs. Transient Services in ASP.NET Core with an example. 

Creating Student Model:
namespace FirstCoreMVCWebApplication.Models
{
    public class Student
    {
        public int StudentId { get; set; }
        public string? Name { get; set; }
        public string? Branch { get; set; }
        public string? Section { get; set; }
        public string? Gender { get; set; }
    }
}
IStudentRepository Interface:
namespace FirstCoreMVCWebApplication.Models
{
    public interface IStudentRepository
    {
        Student GetStudentById(int StudentId);
        List<Student> GetAllStudent();
    }
}
StudentRepository:
namespace FirstCoreMVCWebApplication.Models
{
    public class StudentRepository : IStudentRepository
    {
        //When a new instance of StudentRepository is created,
        //we need to log the Date and time into a text file
        //using the constructor
        public StudentRepository()
        {
            //Please Change the Path to your file path
            string filePath = @"D:\\Projects\\DependencyInjectionDemo\\DependencyInjectionDemo\\wwwroot\\Log\\Log.txt";
            string contentToWrite = $"StudentRepository Object Created: @{DateTime.Now.ToString()}";
            using (StreamWriter writer = new StreamWriter(filePath, true))
            {
                writer.WriteLine(contentToWrite);
            }
        }
        public List<Student> DataSource()
        {
            return new List<Student>()
            {
                new Student() { StudentId = 101, Name = "James", Branch = "CSE", Section = "A", Gender = "Male" },
                new Student() { StudentId = 102, Name = "Smith", Branch = "ETC", Section = "B", Gender = "Male" },
                new Student() { StudentId = 103, Name = "David", Branch = "CSE", Section = "A", Gender = "Male" },
                new Student() { StudentId = 104, Name = "Sara", Branch = "CSE", Section = "A", Gender = "Female" },
                new Student() { StudentId = 105, Name = "Pam", Branch = "ETC", Section = "B", Gender = "Female" }
            };
        }

        public Student GetStudentById(int StudentId)
        {
            return DataSource().FirstOrDefault(e => e.StudentId == StudentId) ?? new Student();
        }

        public List<Student> GetAllStudent()
        {
            return DataSource();
        }
    }
}

Note: We log the object creation date and time within the Constructor to a text file. This will tell us how many times the object of the Student Repository class is created.

SomeOtherService:

The Student Repository can be consumed from a controller or other services as well. So, let us create a service that will consume the Student Repository service. So, create a class file with the name SomeOtherService.cs and then copy and paste the following code:

namespace FirstCoreMVCWebApplication.Models
{
    public class SomeOtherService
    {
        //Create a reference variable of IStudentRepository
        private readonly IStudentRepository? _repository = null;

        //Initialize the variable through constructor
        public SomeOtherService(IStudentRepository repository)
        {
            _repository = repository;
        }

        public void SomeMethod()
        {
            //This Method is also going to use the StudentRepository Service
        }
    }
}
Home Controller:

Please modify the Home Controller as follows. Here, we inject the services of Student Repository and SomeOtherService through the constructor, and within the Index and GetStudentDetails, we invoke the methods of both services.

using FirstCoreMVCWebApplication.Models;
using Microsoft.AspNetCore.Mvc;
namespace FirstCoreMVCWebApplication.Controllers
{
    public class HomeController : Controller
    {
        //Create a reference variable of IStudentRepository
        private readonly IStudentRepository? _repository = null;
        private readonly SomeOtherService? _someOtherService = null;

        //Initialize the variable through constructor
        public HomeController(IStudentRepository repository, SomeOtherService someOtherService)
        {
            _repository = repository;
            _someOtherService = someOtherService;
        }

        public JsonResult Index()
        {
            List<Student>? allStudentDetails = _repository?.GetAllStudent();
            _someOtherService?.SomeMethod();

            return Json(allStudentDetails);
        }

        public JsonResult GetStudentDetails(int Id)
        {
            Student? studentDetails = _repository?.GetStudentById(Id);
            _someOtherService?.SomeMethod();
            return Json(studentDetails);
        }
    }
}

Singleton Service in ASP.NET Core Dependency Injection

The Singleton lifetime option is used when a single instance of a service is required and shared across the application. This is often used for services that provide shared resources, stateless services, or services that require expensive initialization, which you don’t want to repeat. So, let us first understand the singleton service with our example. So, add the following code to the Program.cs class file to register the SomeOtherService and StudentRepository service as Singleton.

builder.Services.AddSingleton<IStudentRepository, StudentRepository>();
builder.Services.AddSingleton<SomeOtherService>();

Singleton service means only one instance of the service is available throughout the application lifetime, no matter how many times and how many requests are sent to the server. In our application, the SomeOtherService also uses the StudentRepository service.

When we send a request to our application (e.g., to the Index or GetStudentDetails method), it creates singleton instances of both the SomeOtherService and StudentRepository services and uses the same StudentRepository service within the SomeOtherService.

Run the application, access the Index or GetStudentDetails method, and verify the log file. If you open the log file, you will see the following: Only one object is created for the StudentRepository service. This proves that a singleton service means only one instance of the service is available throughout the application’s lifetime.

Singleton Service in ASP.NET Core Dependency Injection

Scoped Service in ASP.NET Core Dependency Injection

The Scoped services in ASP.NET Core Dependency Injection are useful in scenarios where you need a service instance that is created once per client request. This is especially relevant in web applications where each HTTP request can be considered a separate scope. So, let us first understand the scoped service from our example. So, add the following code to the Program.cs class file to register the SomeOtherService and StudentRepository services as Scoped.

builder.Services.AddScoped<IStudentRepository, StudentRepository>();
builder.Services.AddScoped<SomeOtherService>();

Scoped service means one instance per HTTP request. In our example, when we send a request to our application (i.e., either to the Index or GetStudentDetails method), it will create a scoped instance of both SomeOtherService and StudentRepository service and use the same StudentRepository service within the SomeOtherService.

First, clear the log file, run the application, access the Index or GetStudentDetails method, and verify the log file. If you open the log file, you will see the following: Two objects are created for the StudentRepository service. This proves that scoped service means one instance per HTTP request. It will create one instance when we access the Index method and another instance when we access the GetStudentDetails method. Internally, when it accesses the SomeOtherService, it will use the same StudentRepository instance.

Scoped Service in ASP.NET Core Dependency Injection

Transient Service in ASP.NET Core Dependency Injection

The Transient services in ASP.NET Core Dependency Injection are designed for situations where you want a new service instance whenever requested. So, let us understand Transient’s service with our example. So, add the following code to the Program.cs class file to register the SomeOtherService and StudentRepository services as Transient.

builder.Services.AddTransient<IStudentRepository, StudentRepository>();
builder.Services.AddTransient<SomeOtherService>();

Transient service means a new service instance is created whenever requested. In our example, when we send a request to our application (i.e., either to the Index or GetStudentDetails method), it will create new instances of both SomeOtherService and StudentRepository service; further, it will create a new StudentRepository instance when we request it in the SomeOtherService.

First, clear the log file, run the application, access the Index or GetStudentDetails method, and verify the log file. If you open the log file, you will see the following: Four objects are created for the StudentRepository service. This proves that the Transient service means a new service instance whenever requested. In our example, when we access the Index action method, it will create one instance within the Home Controller and another instance within the SomeOtherService, and this will happen again when we call the GetStudentDetails action method.

Transient Service in ASP.NET Core Dependency Injection

Singleton vs. Scoped vs. Transient Services in ASP.NET Core

In ASP.NET Core, the dependency injection container provides three different service lifetimes for registering services: Singleton, Scoped, and Transient. Understanding the differences between them is crucial for designing your application correctly. Here’s a breakdown of each:

Singleton Services:
  • Lifetime: Created and shared for the lifetime of the application. Only one instance of the singleton service is created and shared across all requests and classes that depend on it.
  • Use Case: Ideal for services that maintain a global state or provide a service that doesn’t change state in any way that affects subsequent requests (e.g., configuration services).
  • Considerations: Since they are shared, singletons need to be thread-safe. If not managed correctly, they can also lead to issues like holding onto resources for a long time or memory leaks. They’re not suitable for multi-threaded scenarios where the service might maintain a state that changes during a request.
Scoped Services:
  • Lifetime: Created once per client request (or per scope). A new instance is provided for each HTTP request.
  • Use Case: This is perfect for services that need to maintain state within a single request, such as data access services in a web application (e.g., DbContext in Entity Framework).
  • Considerations: It is not suitable for sharing data between different requests but is excellent for data specific to a single operation or request. The data should be disposed of after the request is completed.
Transient Services:
  • Lifetime: Created each time they are requested from the service container. This means a new instance is created each time a class or method requests this service.
  • Use Case: This is best for lightweight, stateless services. These are often small services that do a specific job and don’t retain any state between calls.
  • Considerations: Since a new instance is created every time, it can be resource-intensive if the service is large or complex, and it might lead to performance issues.
Key Differences:
  • Lifetime: Singleton (once per app), Scoped (once per request), Transient (each time requested).
  • State: Singleton (shared state), Scoped (request-specific state), Transient (no shared state).

In the next article, I will discuss Creating an ASP.NET Core Application using the MVC Template with Examples. In this article, I try to explain Singleton vs. Scoped vs. Transient Services in ASP.NET Core Application with examples. I hope this article will help you understand Singleton vs. Scoped vs. Transient Services in ASP.NET Core Application. 

Leave a Reply

Your email address will not be published. Required fields are marked *