Dependency Injection - Service Lifetimes

Introduction
 
The article aims to give a quick understanding of the Service Lifetimes for Dependency Injection in .NET Core. The article does not cover what DI is or how it works in .NET Core.
 
Short Description Of Dependency Injection
 
Dependency Injection (DI) is a pattern that can help developers decouple the different pieces of their applications. When a system is designed to use DI, with many classes requesting their dependencies via their constructor (or properties), it's helpful to have a class dedicated to creating these classes with their associated dependencies. These classes are referred to as containers, or more specifically, Inversion of Control (IoC) containers or Dependency Injection (DI) containers. A container is essentially a factory that's responsible for providing instances of types that are requested from it.
 
Microsoft .NET Core framework
 
Microsoft .NET Core includes a simple built-in container (represented by the IServiceProvider interface) that supports constructor injection. The NameSpace under which it is included is Microsoft.Extensions.DependencyInjection.
 
Dependency Lifetimes

At registration time, dependencies require a lifetime definition. The service lifetime defines the conditions under which a new service instance will be created. Below are the lifetimes defined by the .NET Core DI framework.
  1. Transient
    Created every time they are requested

  2. Scoped
    Created once per scope; i.e., web request.or any unit of work

  3. Singleton
    Created only for the first request. If a particular instance is specified at registration time, this instance will be provided to all consumers of the registration type.
Let's understand the lifetimes using a simple snippet of code for console application below.
  1. public interface IService  
  2.    {  
  3.        void Info();  
  4.    }  
  5.   
  6.    public interface ISingleton : IService { }  
  7.    public interface IScoped : IService { }  
  8.    public interface ITransient : IService { }  
  9.   
  10.    public abstract class Operation : ISingleton, IScoped, ITransient  
  11.    {  
  12.        private Guid _operationId;  
  13.        private string _lifeTime;  
  14.   
  15.        public Operation(string lifeTime)  
  16.        {  
  17.            _operationId = Guid.NewGuid();  
  18.            _lifeTime = lifeTime;  
  19.   
  20.            Console.WriteLine($"{_lifeTime} Service Created.");  
  21.        }  
  22.   
  23.        public void Info()  
  24.        {  
  25.            Console.WriteLine($"{_lifeTime}: {_operationId}");  
  26.        }  
  27.    }  
  28.   
  29.    public class SingletonOperation : Operation  
  30.    {  
  31.        public SingletonOperation() : base("Singleton") { }  
  32.    }  
  33.    public class ScopedOperation : Operation  
  34.    {  
  35.        public ScopedOperation() : base("Scoped") { }  
  36.    }  
  37.    public class TransientOperation : Operation  
  38.    {  
  39.        public TransientOperation() : base("Transient") { }  
  40.    }  
  41.   
  42.    class Program  
  43.    {  
  44.        static void Main(string[] args)  
  45.        {  
  46.   
  47.            var serviceProvider = new ServiceCollection()  
  48.                .AddTransient<ITransient, TransientOperation>()  
  49.                .AddScoped<IScoped, ScopedOperation>()  
  50.                .AddSingleton<ISingleton, SingletonOperation>()  
  51.                .BuildServiceProvider();  
  52.   
  53.   
  54.            Console.WriteLine("========== Request 1 ============");  
  55.            serviceProvider.GetService<ITransient>().Info();  
  56.            serviceProvider.GetService<IScoped>().Info();  
  57.            serviceProvider.GetService<ISingleton>().Info();  
  58.            Console.WriteLine("========== ========= ============");  
  59.   
  60.            Console.WriteLine("========== Request 2 ============");  
  61.            serviceProvider.GetService<ITransient>().Info();  
  62.            serviceProvider.GetService<IScoped>().Info();  
  63.            serviceProvider.GetService<ISingleton>().Info();  
  64.            Console.WriteLine("========== ========= ============");  
  65.   
  66.   
  67.            using (var scope = serviceProvider.CreateScope())  
  68.            {  
  69.                Console.WriteLine("========== Request 3 ============");  
  70.                scope.ServiceProvider.GetService<IScoped>().Info();  
  71.                scope.ServiceProvider.GetService<ITransient>().Info();  
  72.                scope.ServiceProvider.GetService<ISingleton>().Info();  
  73.                Console.WriteLine("========== ========= ============");  
  74.   
  75.                Console.WriteLine("========== Request 4 ============");  
  76.                scope.ServiceProvider.GetService<IScoped>().Info();  
  77.                scope.ServiceProvider.GetService<ISingleton>().Info();  
  78.                Console.WriteLine("========== ========= ============");  
  79.            }  
  80.   
  81.            using (var scope = serviceProvider.CreateScope())  
  82.            {  
  83.                Console.WriteLine("========== Request 5 ============");  
  84.                scope.ServiceProvider.GetService<IScoped>().Info();  
  85.                scope.ServiceProvider.GetService<ISingleton>().Info();  
  86.                Console.WriteLine("========== ========= ============");  
  87.            }  
  88.   
  89.   
  90.            Console.WriteLine("========== Request 6 ============");  
  91.            serviceProvider.GetService<IScoped>().Info();  
  92.            Console.WriteLine("========== ========= ============");  
  93.   
  94.            Console.ReadKey();  
  95.        }  
  96.   
  97.   
  98.    } 
Sample Output
  1. ========== Request 1 ============  
  2. Transient Service Created.  
  3. Transient: 6958dca3-05a7-4322-82b6-dec6fcacefeb  
  4. Scoped Service Created.  
  5. Scoped: c1098528-5855-46f7-a284-ba2a1dff0769  
  6. Singleton Service Created.  
  7. Singleton: 5ca7706b-5181-49ff-be09-5493f0decc0d  
  8. ========== ========= ============ 

  1. ========== Request 2 ============  
  2. Transient Service Created.  
  3. Transient: c7710765-0b09-43bd-86f4-0ef022ed5220  
  4. Scoped: c1098528-5855-46f7-a284-ba2a1dff0769  
  5. Singleton: 5ca7706b-5181-49ff-be09-5493f0decc0d  
  6. ========== ========= ============  

  1. ========== Request 3 ============  
  2. Scoped Service Created.  
  3. Scoped: 6d5d7842-468f-4040-907c-6b2ab907df61  
  4. Transient Service Created.  
  5. Transient: 1ec15435-dda2-4fe0-8e29-c154c0352c95  
  6. Singleton: 5ca7706b-5181-49ff-be09-5493f0decc0d  
  7. ========== ========= ============  

  1. ========== Request 4 ============  
  2. Scoped: 6d5d7842-468f-4040-907c-6b2ab907df61  
  3. Singleton: 5ca7706b-5181-49ff-be09-5493f0decc0d  
  4. ========== ========= ============  

  1. ========== Request 5 ============  
  2. Scoped Service Created.  
  3. Scoped: 2ad1a715-09b0-4aac-9b84-2df376cc2223  
  4. Singleton: 5ca7706b-5181-49ff-be09-5493f0decc0d  
  5. ========== ========= ============ 

  1. ========== Request 6 ============  
  2. Scoped: c1098528-5855-46f7-a284-ba2a1dff0769  
  3. ========== ========= ============  
Output Analysis 
 
Transient - (Request 1,2,3)

In the above output, we can see that a new instance transient service is created whenever the service is requested.
 
Singleton - (Request 1,2,3,4,5)

In the above output, we can see that a singleton service is only created when it is called for the first time. In the next subsequent requests, the same instance is provided. 
 
Scope - (Request 1,2,3,4,5,6)

In the sample program, we have 3 scopes,
  1. For the Main function
  2. For Request 3 & 4
  3. For Request 5
In the above output, we can see that a scoped service is only created when it is called for the first time in a particular scope. In short, it works like a Singleton service within a given scope.