DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Dependency Injection in Spring
  • Dropwizard vs. Micronaut: Unpacking the Best Framework for Microservices
  • Building Web Applications With .NET: Best Practices and Techniques
  • Effective Java Collection Framework: Best Practices and Tips

Trending

  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • Power BI Embedded Analytics — Part 2: Power BI Embedded Overview
  • Java Virtual Threads and Scaling
  • Performance Optimization Techniques for Snowflake on AWS
  1. DZone
  2. Coding
  3. Frameworks
  4. Understanding the Dependency Injection Lifecycle: Singleton, Scoped, and Transient With Detailed Examples

Understanding the Dependency Injection Lifecycle: Singleton, Scoped, and Transient With Detailed Examples

This article will help you gain a solid understanding of the lifecycle of singleton, scoped, and transient services when implementing dependency injection.

By 
Rishi Mohan user avatar
Rishi Mohan
·
Oct. 03, 24 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
5.9K Views

Join the DZone community and get the full member experience.

Join For Free

Developers may be aware of the lifecycle of service instances when using dependency injection, but many don’t fully grasp how it works. You can find numerous articles online that clarify these concepts, but they often just reiterate definitions that you might already know. Let me illustrate with a detailed example that simplifies the explanation.

When implementing dependency injection, developers have three options that determine the lifecycle of the instances:

  1. Singleton
  2. Scoped
  3. Transient

While most developers recognize these terms, a significant number struggle to determine which option to choose for a service's lifetime.

Definitions

Let me start with definitions:

  • Singleton lifetime service instances are created once per application from the service container. A single instance will serve all subsequent requests. Singleton services are disposed of at the end of the application (i.e., upon application restart).
  • Transient lifetime service instances are created per request from the service container. Transient services are disposed of at the end of the request.
  • Scoped lifetime service instances are created once per client request. Transient services are disposed of at the end of the request.

When to Use

  • Singleton - When you want to use single instances of services throughout the life cycle of the application
  • Transient - When you want to use individual instances of services within the client request
  • Scoped - When you want to use a single instance of service for each request

What is a client request? In very simple words, you can consider it as an API/REST call coming to your application by button clicks of the user to get the response.

Don’t worry, let's understand with an example.

Example

First, let's create interfaces/services and classes:

C#
 
	// we are declaring 3 services as below
		Public interface ISingleton
		Public interface ITransient 
		Public interface IScoped 


Now let's write the implementation for each service Interface/service created above. We will try to understand the concept by trying to update the callMeSingleton, callMeTransient, and callMeScoped variable.

  • Singleton class implementation:
C#
 
class SingletonImplementation: ISingleton
{
	var callMeSingleton = ""

	// other implementation
	public SetSingleton(string value)
	{
		callMeSingleton = value;
	}
	// other implementation
}


  • Transient class implementation:
C#
 
class TransientImplementation: ITransient 
{
	var callMeTransient = ""
	
	// other implementation
	public SetTransient(string value)
	{
		callMeTransient = value;
	}
	// other implementation
}


  • Scoped class implementation:
C#
 
class ScopedImplementation: IScoped 
{
	var callMeScoped = ""
			
	//other implementation
	public SetScoped(string value)
	{
		callMeScoped = value;
	}
	//other implementation		
}


Let's register (ConfigureServices) with DI (Dependency Injection) to decide the life cycle of each service instance:

C#
 
services.AddSingleton<ISingleton, SingletonImplementation>();
services.AddTransient<ITransient , TransientImplementation>();
services.AddScoped<IScoped , ScopedImplementation>();


Let's use/call these services from 3 different classes (ClassA, ClassB, and ClassC) to understand the life cycle of each service:

  • ClassA:
C#
 
public class ClassA
{
	private ISingleton _singleton;
	//constructor to instantiate 3 different services we creates
	public ClassA(ISingleton singleton,
                  ITransient _transient,
                  IScoped _scoped)
	{
		_singleton = singleton;
	}
		
	public void UpdateSingletonFromClassA()
	{
		_singleton.SetSingleton("I am from ClassA");
	} 
	
  	public void UpdateTransientFromClassA()
    {
		_transient.SetTransient("I am from ClassA");
	} 
	
  	public void UpdateScopedFromClassA()
	{
		_scoped.SetScoped("I am from ClassA");
	}

	// other implementation 
}


  • ClassB:
C#
 
public class ClassB
{
	private ISingleton _singleton;
	//constructor to instantiate 3 different services we creates
	public ClassB(ISingleton singleton,
                  ITransient _transient,
                  IScoped _scoped)
	{
		_singleton = singleton;
	}
		
	public void UpdateSingletonFromClassB()
	{
		_singleton.SetSingleton("I am from ClassB");
	} 
	
  	public void UpdateTransientFromClassB()
    {
		_transient.SetTransient("I am from ClassB");
	} 
	
  	public void UpdateScopedFromClassB()
	{
		_scoped.SetScoped("I am from ClassB");
	}

	// other implementation 
}


  • ClassC:
C#
 
public class ClassC
{
	private ISingleton _singleton;
	//constructor to instantiate 3 different services we creates
	public ClassC(ISingleton singleton,
                  ITransient _transient,
                  IScoped _scoped)
	{
		_singleton = singleton;
	}
		
	public void UpdateSingletonFromClassC()
	{
		_singleton.SetSingleton("I am from ClassC");
	} 
	
  	public void UpdateTransientFromClassC()
    {
		_transient.SetTransient("I am from ClassC");
	} 
	
  	public void UpdateScopedFromClassC()
	{
		_scoped.SetScoped("I am from ClassC");
	}

	// other implementation 
}


Analysis

Let's analyze the results and behavior for each life cycle one by one from the above implementation:

Singleton

All the classes (ClassA, ClassB, and ClassC) will use the same single instance of the SingletonImplementation class throughout the lifecycle of the application. This means that properties, fields, and operations of the SingletonImplementation class will be shared among instances used on all calling classes. Any updates to properties or fields will override previous changes.

For example, in the code above, ClassA, ClassB, and ClassC are all utilizing the SingletonImplementation service as a singleton instance and calling SetSingleton to update the callMeSingleton variable. In this case, there will be a single value of the callMeSingleton variable for all requests trying to access this property. Whichever class accesses it last to update will override the value of callMeSingleton.

  • ClassA - It will have its same instance as other classes for service TransientImplementation. 
  • ClassB - It will have its same instance as other classes for service TransientImplementation.
  • ClassC - It will have its same instance as other classes for service TransientImplementation.

ClassA, ClassB, and ClassC are updating the same instance of the SingletonImplementation class, which will override the value of callMeSingleton. Therefore, be careful when setting or updating properties in the singleton service implementation.

Singleton services are disposed of at the end of the application (i.e., upon application restart).

Transient

All the classes (ClassA, ClassB, and ClassC) will use their individual instances of the TransientImplementation class. This means that if one class calls for properties, fields, or operations of the TransientImplementation class, it will only update or override its individual instance values. Any updates to properties or fields are not shared among other instances of TransientImplementation.

Let's understand:

  • ClassA - It will have its own instance of service of TransientImplementation.
  • ClassB - It will have its own instance of service of TransientImplementation.
  • ClassC - It will have its own instance of service of TransientImplementation. 

Let's say you have a ClassD which is calling transient service from ClassA, ClassB, and ClassC instances. In this case, each class instance would be treated as different/separate instance and each class would have its own value of callMeTransient. Read the inline comments below for  ClassD:

C#
 
public ClassD
{
	// other implementation
		
    // Below line of code will update the value of callMeTransient to "I am from ClassA" for the intance of ClassA only.
    // And it will not be changed by any next calls from Class B or B class
	ClassA.UpdateTransientFromClassA(); 		
       
    // Below line of code will update the value of callMeTransient to "I am from ClassB" for the intance of ClassB only.
    // And it will neither override the value for calssA instance nor will be changed by next call from Class C
	ClassB.UpdateTransientFromClassB(); 
    
    // Below line of code will update the value of callMeTransient to "I am from ClassC" for the intance of ClassC only.
    // And it will neither override the value for calssA and classB instance nor will be changed by any next call from any other class.
    ClassC.UpdateTransientFromClassC(); 

	// other implementation
}


Transient services are disposed at the end of each request. Use Transient when you want a state less behavior within the request.

Scoped

All the classes (ClassA, ClassB, and ClassC) will be using single instances of ScopedImplementation class for each request. This means that calls for properties/fields/operations on ScopedImplementation class will happen on single instance with in the scope of request. Any updates of properties/fields will be shared among other classes.   

Let's understand:

  • ClassA - It will have its instance of service of TransientImplementation. 
  • ClassB - It will have its same instance of service of TransientImplementation as ClassA.
  • ClassC - It will have its same instance of service of TransientImplementation as ClassA and ClassB.

Let's say you have a ClassD which is calling scoped service from ClassA, ClassB, and ClassC instances. In this case, each class will have single instance of ScopedImplementation class. Read the inline comments for ClassD below.

C#
 
public class ClassD
{
  // other implementation
 
  // Below code will update the value of callMeScoped to "I am from ClassA" for the instance of ClassA
  // But as it is Scoped life cycle so it is holding single instance ScopedImplementation of
  // Then it can be overridden by next call from ClassB or ClassC
	ClassA.UpdateScopedFromClassA();  
  
  // Below code will update the value of callMeScoped to "I am from ClassB" for single instance ScopedImplementation 
  // And it will override the value of callMeScoped for classA instance too. 
	ClassB.UpdateScopedFromClassB();
  
  // Now if Class A will perform any operation on ScopedImplementation,
  // it will use the latest properties/field values which are overridden by classB.
	
  // Below code will update the value of callMeScoped to "I am from ClassC"
  // And it will override the value of callMeScoped for classA and ClassB instance too.
	ClassC.UpdateScopedFromClassC(); 
  // now if Class B or Class A will perform any operation on ScopedImplementation , it will use the latest properties/field values which are overridden by classC

    // other implementation
}


Scoped services are disposed at the end of each request. Use Scoped when you want a stateless behavior between individual requests.

Trivia Time

The lifecycle of a service can be overridden by a parent service where it gets initialized. Confused? Let me explain:

Let's take the same example from above classes and initialize the Transient and Scoped services from SingletonImplementation (which is a singleton) as below. That would initiate the ITransient and IScoped services and overwrite the lifecycle of these to singleton life cycle as parent service. In this case your application would not have any Transient or Scoped services (considering you just have these 3 services we were using in our examples). 

Read through the lines in the below code:

C#
 
public class SingletonImplementation: ISingleton
{
	// constructor to add initialize the services.
	private readonly ITransient _transient 
	private readonly IScoped _scoped 
		
	SingletonImplementation(ITransient transient, IScoped scoped)
	{
		_transient = transient;  
        // Now _transient would behave as singleton service irrespective of how it was registered as Transient
		_scoped = scoped;
        // now scoped  would behave as singleton service irrespective of it being registered as Scoped
	}
    var callMeSingleton = ""
		
	// other implementation
}


Summary

I hope the above article is helpful in understanding the topic. I would recommend try it yourself with the context set above and you will never be confused again. Singleton is the easiest to understand because once you create its instance, it will be shared across applications throughout the lifecycle of the application. On the similar lines of Singleton, Scoped instances mimic the same behavior but only throughout the lifecycle of a request across application. Transient is totally stateless, for each request and each class instance will hold its own instance of serivice.

Dependency injection Implementation Application lifecycle management Singleton pattern Framework

Opinions expressed by DZone contributors are their own.

Related

  • Dependency Injection in Spring
  • Dropwizard vs. Micronaut: Unpacking the Best Framework for Microservices
  • Building Web Applications With .NET: Best Practices and Techniques
  • Effective Java Collection Framework: Best Practices and Tips

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!