Skip to main content

ASP.net Core Services Lifetime Differences Between Using AddTransient, AddScoped and AddSingleton Services

 

ASP.net Core Services Lifetime Differences Between Using AddTransient, AddScoped and AddSingleton Services by Ziggy Rafiq




ASP.net Core Services Lifetime Differences Between Using AddTransient, AddScoped and AddSingleton Services


Overview

In this blog post, I will be explaining to you the ASP.net core Service Lifetime and will be explaining to you what is the difference between them and which one can be a risk to a memory leak if used incorrectly.

AddTransient, AddScoped, and AddSingleton are dependency injection services in ASP.NET Core. The differences between them are as follows below.

  • AddTransient: A new instance of the service is created every time it is requested.
  • AddScoped: A single instance of the service is created per request within the same scope.
  • AddSingleton: A single instance of the service is created for the entire application lifetime.
In general, it's recommended to use AddScoped for services that are stateful and AddTransient for services that are stateless or have a short lifetime. AddSingleton is usually used for services that are expensive to create or only need to be created once for the entire application.

 

ASP.net Core Service Lifetime diagram by Ziggy Rafiq



How to Use AddTransient, AddScoped, and AddSingleton

Below are some general guidelines on when to use each of the AddTransient, AddScoped, and AddSingleton services in ASP.NET Core in the program.cs file after the var builder = WebApplication.CreateBuilder(args);  if using .net 6 and above for the .net 5 and below it is in StartUp.cs with in the public void ConfigureServices(IServiceCollection services).

  • AddTransient: Use AddTransient when you need a new instance of the service each time it's requested. This is useful for services that are lightweight and stateless, such as those used for logging or for sending emails. Use AddTransient for services that are stateless, lightweight, and have a short lifetime. Examples include services that generate data, perform calculations, or interact with external APIs.
  • AddScoped: Use AddScoped when you want a single instance of the service to be shared within a single request. This is useful for services that are stateful, such as those that hold data specific to the current request. Use AddScoped for services that have a request-level lifetime and maintain some state. Examples include services that access the database, perform caching, or maintain user-specific data
  • AddSingleton: Use AddSingleton when you want a single instance of the service to be shared throughout the entire application. This is useful for services that are expensive to create or that need to maintain the state between requests, such as those used for caching. Use AddSingleton for services that have a singleton lifetime and are used throughout the application. Examples include services that provide global application configuration, logging, or singleton services that must only be instantiated once.

Recommend Way, You should aim to use AddTransient and AddScoped services as much as possible, and reserve AddSingleton for services that need to maintain state or that are expensive to create. By using the appropriate service lifetime, you can improve the performance and scalability of your application.

It's important to understand that using AddSingleton can introduce potential memory leaks and synchronization issues if not used correctly, as the same instance is shared across all requests.

Reminder, it's recommended to choose the lifetime based on the requirements of your service and the desired behavior of your application.


Code Examples

In the code examples below I have given you examples of how to use the AddSingleton, AddTransient and AddScoped and explained what each one of them means and when it's best to use them.

AddSingleton creates a single instance of a service that is shared by all consumers throughout the application's lifetime. Use AddSingleton when you want to create a single instance of a service that is shared across the entire application.

builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>(); (.Net 6 and above)

services.AddSingleton< IExampleSingletonService, ExampleSingletonService >(); (.Net 5 and below)

AddTransient creates a new instance of a service every time it is requested. Use AddTransient when you want to create a new instance of a service every time it is requested.

builder.Services. AddTransient<IExampleTransientService, ExampleTransientService>(); (.Net 6 and above)

services.AddTransient<IExampleTransientService, ExampleTransientService>();  (.Net 5 and below)

 AddScoped creates a single instance of a service that is shared within the scope of a single client request. Use AddScoped when you want to create a single instance of a service that is shared within the scope of a single client request.

builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>(); (.Net 6 and above)

services.AddScoped<IExampleScopedService, ExampleScopedService>(); (.Net 5 and below)

Final Thoughts

In this blog post I have explained about AddSingleton, AddTransient and AddScoped and when to use each one of them and also have shared code sample of using AddSingleton, AddTransient and AddScoped. I have point out the riskes of using AddSingleton and reminded you about using AddSingleton, AddTransient and AddScoped based on your development of the application needs also I have recommended why to use AddTransient and AddScoped. 

Comments

Most Viewed Ziggy Rafiq Blog Posts

How to use Enum Data Values with .Net 6.0 Framework and Entity Framework Core 6

How to use Enum Data Values with .Net 6.0 Framework and Entity Framework Core 6 Overview An Enum (Enumeration) is a group of constants that are read-only value types. By default, the first value of the Enum variable is 0 i.e. Here we will create an Enum of Priorities type with read-only values of Highest, Normal and Low. We will set the read-only values using an integer assigning a number next to the value. By default, the integer value will start with 0. Here we will be assigning the integer value next to the Enum value such as in the below example and we will use a comma (,) to separate the item in the list of Enum(Enumeration).  We create Enum by using the Enum keyword and then using class, interface, and abstract. The reason we use an Enum is to ensure we improve our application performance and improve application readability, and maintainability, and reduces the complexity of the application hence why if you take a look at the example below of Status (NotStarted, Started, Complete

A Complete Guide to Using GUIDs in C# with Code Examples

  Overview In this post, we are looking at GUIDs (Globally Unique Identifiers), which are widely used in C# for generating unique identifiers for objects, entities, and resources in a system. In this post, we'll explore the basics of GUIDs in C#, their advantages, and how to convert strings to GUIDs. In this post, we have used Guid Generator to create the GUID from the following URL Address https://guidgenerator.com/ What is GUID GUID (Globally Unique Identifier) in C# is a 128-bit value that is used to identify objects, entities, or resources in a unique manner across different systems and applications. It is also known as UUID (Universally Unique Identifier) in some other programming languages.   GUIDs are generated using a combination of unique factors such as the MAC address of the network adapter, the current time and date, and a random number. The resulting GUID is a string of 32 hexadecimal digits separated by hyphens, such as "b86f2096-237a-4059-8329-1bbcea72769b&

Primitives Data Types and None-Primitives Data Types in C# with Code Examples

  Overview I wrote this post to provide an explanation of primitive and non-primitive data types in C#. C# is a strongly typed programming language, where each variable and expression must have a specific data type. C# data types are categorized into two primary groups: primitive data types and non-primitive data types. Primitive data types are the simplest data types available in programming languages. They are typically pre-defined data types and can represent a single value, such as a boolean value, character, or integer. Examples of primitive data types include int, char, float, double, and boolean, which are common in programming languages like C++, C, and Java. Non-primitive data types are also referred to as composite data types or reference data types. They are constructed from primitive data types and are more complex than primitive data types. Non-primitive data types can hold multiple values and allow for the creation of more intricate data structures like tables, lists,