1989

I want to implement dependency injection (DI) in ASP.NET Core. So after adding this code to ConfigureServices method, both ways work.

What is the difference between the services.AddTransient and service.AddScoped methods in ASP.NET Core?

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddScoped<IEmailSender, AuthMessageSender>();
}
boop
  • 7,413
  • 13
  • 50
  • 94
Elvin Mammadov
  • 25,329
  • 11
  • 40
  • 82
  • 224
    @tmg The docs say 'Transient lifetime services are created each time they are requested.' and 'Scoped lifetime services are created once per request.' which unless my grasp of English is weaker than I thought actually mean the exact same thing. – Neutrino Oct 05 '17 at 15:20
  • 3
    @Neutrino It does not. Check the answer [below](https://stackoverflow.com/questions/38138100/what-is-the-difference-between-services-addtransient-service-addscope-and-servi?noredirect=1#answer-38139500) – tmg Oct 05 '17 at 18:50
  • 121
    @tmg I know. I'm just pointing out that the docs aren't at all clear on this point, so pointing people to the docs isn't very helpful. – Neutrino Oct 06 '17 at 09:32
  • 12
    Late to the party, reading the comments even later, but I printed out that article, read it, and jotted the same observation in the margin that I now see @Neutrino made here. The article was ENTIRELY vague in offering that analysis. The example, thankfully, was less confusing. – Wellspring Dec 22 '17 at 21:47
  • 3
    @Neutrino i am probably late to comment. but the Warning is one important difference between transient and scoped. because i banged my head for a while before i found this post and then the article :) WARNING: When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, – Mandar Jogalekar Dec 18 '18 at 14:00
  • 1
    @MandarJogalekar, thank you for your comment. Could you please share some resource about what you said? – Elvin Mammadov Dec 18 '18 at 14:07
  • 1
    @ElvinMammadov it is this link https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options – Mandar Jogalekar Dec 18 '18 at 14:13
  • 90
    As far as I understand: *Transient lifetime services are created each time they are **requested***. The word **requested** here is the everyday English meaning of asking for something, in this case a service. Whereas the word **request** in *once per request* refers to an HTTP Request. But I do understand the confustion. – Memet Olsen Aug 08 '19 at 14:53
  • 2
    here is a sample example to understand the behavior: https://www.tektutorialshub.com/asp-net-core/asp-net-core-dependency-injection-lifetime/ – serge Feb 08 '21 at 19:19
  • 1
    It's a fresh instance each time but can cause memory leaks: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#disposable-transient-services-captured-by-container – Wouter Feb 28 '21 at 20:15
  • 13
    Transients means if `MyClass` needs an instance Of `SomeDependency`, it gets one, and if `SomeOtherClass` needs an instance, it gets a new instance of `SomeDependency`. Which is different from the instance given to `MyClass`. Scoped means (in the context of HTTP requests being handled) that an instance of `SomeDependency`, given to `MyClass` and `SomeOtherClass` will be the same (for the same HTTP request being handled). – Mike de Klerk May 19 '21 at 14:29
  • 2
    If you are a hindi speaker. I have created a very nice video on this topic which explains the concept with expample. https://youtu.be/OkOy8Q-OPGI – Iftikhar Ali Ansari Oct 07 '21 at 06:26
  • 1
    The word **requested** and once **per request** is making those confusions. Though @MemetOlsen made that clear, I'm adding an extra line. The first one is requested for a new instance of the dependency object no matter whether you are on the same HTTP call or not. Later one is the same instance of dependency object across the same HTTP call. – autopilot Jun 05 '22 at 04:01
  • 1
    I always forget this. Its so frustrating – Andy Aug 25 '22 at 08:36

12 Answers12

3208

TL;DR

Transient objects are always different; a new instance is provided to every controller and every service.

Scoped objects are the same within a request, but different across different requests.

Singleton objects are the same for every object and every request.

For more clarification, this example from .NET documentation shows the difference:

To demonstrate the difference between these lifetime and registration options, consider a simple interface that represents one or more tasks as an operation with a unique identifier, OperationId. Depending on how we configure the lifetime for this service, the container will provide either the same or different instances of the service to the requesting class. To make it clear which lifetime is being requested, we will create one type per lifetime option:

using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }

    public interface IOperationScoped : IOperation
    {
    }

    public interface IOperationSingleton : IOperation
    {
    }

    public interface IOperationSingletonInstance : IOperation
    {
    }
}

We implement these interfaces using a single class, Operation, that accepts a GUID in its constructor, or uses a new GUID if none is provided:

using System;
using DependencyInjectionSample.Interfaces;
namespace DependencyInjectionSample.Classes
{
    public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton, IOperationSingletonInstance
    {
        Guid _guid;
        public Operation() : this(Guid.NewGuid())
        {

        }

        public Operation(Guid guid)
        {
            _guid = guid;
        }

        public Guid OperationId => _guid;
    }
}

Next, in ConfigureServices, each type is added to the container according to its named lifetime:

services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();

Note that the IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty, so it will be clear when this type is in use. We have also registered an OperationService that depends on each of the other Operation types, so that it will be clear within a request whether this service is getting the same instance as the controller, or a new one, for each operation type. All this service does is expose its dependencies as properties, so they can be displayed in the view.

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}

To demonstrate the object lifetimes within and between separate individual requests to the application, the sample includes an OperationsController that requests each kind of IOperation type as well as an OperationService. The Index action then displays all of the controller’s and service’s OperationId values.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // ViewBag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // Operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

Now two separate requests are made to this controller action:

First Request

Second Request

Observe which of the OperationId values varies within a request, and between requests.

  • Transient objects are always different; a new instance is provided to every controller and every service.

  • Scoped objects are the same within a request, but different across different requests

  • Singleton objects are the same for every object and every request (regardless of whether an instance is provided in ConfigureServices)

David Pine
  • 23,787
  • 10
  • 79
  • 107
akardon
  • 43,164
  • 4
  • 34
  • 42
  • 114
    I understood the functions of each of them, but can someone explain the impact of using one instead of the other. What issues may it cause if not used correctly or choose one instead of another. – pawan nepal Aug 02 '17 at 02:15
  • 27
    Say you are creating a request context related object (like the current user) with singleton scope then it's gonna remain the same instance across all the http requests which is not desired. IOC is all about creating instances, so we need to specify what's the scope of the created instance. – akardon Aug 02 '17 at 14:58
  • 5
    Could you also explain the common pitfalls in which we nest transient or scoped dependencies in a singleton? – Second Person Shooter Jan 14 '20 at 01:28
  • 65
    fair point! generally speaking if we put an object with a shorter lifetime in a longer living object, the IoC wouldn't create the inner object again. so say if you have a singleton which has a transient or scoped object in it, the inner object doesn't get recreated as the constructor of the singleton wouldn't get called again. but the other way around is OK. you can put a singleton in a transient with no problem. so the rule of thumb is the inner object should have an equal or longer lifetime than the outer one. – akardon Jan 16 '20 at 11:01
  • 1
    `Scoped objects are the same within a request, but different across different requests.` I thought that in order to complete a request a new object of a controller should be created and then a respective method of the object should be called. But the statement about the meaning of the `scoped services` contradicts this. Could you help me figure this out, please? I mean during a single request the DI happens only once, so there is no point in either the description of the `scoped services` or in my understanding of the request handling. Or I am missing something here. – manymanymore May 09 '20 at 18:42
  • 2
    @YaroslavTrofimov To be honest I didn't get the contradiction part, yeah that's true, each request creates a new instance of the controller and calls its pertinent method but the services that the controller's relaying on (the operations) could be in different scopes. which is fine. think of it this way, let's say in the controller method there is a loop in which each iteration needs to create a new instance of an object, if that object's lifetime is scoped it's gonna create the object once and reuse it multiple time, whereas if it was transient it would create multiple objects, – akardon May 12 '20 at 07:41
  • 24
    @akazemis doing the Lord's work here...clear and concise explanation and graphics w/ guid examples drive the illustration home. Thank you! – snejame Jan 15 '21 at 14:16
  • 1
    @akazemis is the only reason you are adding both the interface and its implementation, e.g. `services.AddTransient();` so that you can inject any implementation of that interface into a class or struct? In other words, why do `services.AddTransient();` instead of just `services.AddTransient();`? The latter means that if you want to change the class (i.e. `Operation`), then you have to change code in all constructors where the parameter is `Operation operation`. – bit May 05 '21 at 10:23
  • 1
    but if you use `` then you only have to change the class reference (e.g. `Operation`) in one place, i.e. in the statement `services.AddTransient();`. – bit May 05 '21 at 10:30
  • 1
    This is probably the best single answer I've seen on SO to any question, ever. Thank you – Chris Mar 23 '23 at 12:45
  • @pawannepal i can give you an example. I was creating a real-time private chat app with signalR. It was using singleton and I can only send one message. Every time I tried to send another it wouldn't save to the database. I switched it to scoped and it worked how it suppose to. – Anime Zone Jun 01 '23 at 22:00
547

In .NET's dependency injection there are three major lifetimes:

Singleton which creates a single instance throughout the application. It creates the instance for the first time and reuses the same object in the all calls.

Scoped lifetime services are created once per request within the scope. It is equivalent to a singleton in the current scope. For example, in MVC it creates one instance for each HTTP request, but it uses the same instance in the other calls within the same web request.

Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.

Here you can find and examples to see the difference:

ASP.NET 5 MVC6 Dependency Injection in 6 Steps (web archive link due to dead link)

Your Dependency Injection ready ASP.NET : ASP.NET 5

And this is the link to the official documentation:

Dependency injection in ASP.NET Core

Crypth
  • 1,576
  • 18
  • 32
akardon
  • 43,164
  • 4
  • 34
  • 42
  • 46
    Could you please explain why the Transient is the most lightweight? I thought the Transient is the most heavy work because it needs to create an instance every time for every injection. – Expert wanna be Jul 20 '16 at 10:22
  • 34
    You're right. Transient is not the most lightweight, I just said it's suitable for lightweight RESTful services :) – akardon Jul 20 '16 at 22:40
  • 6
    So in which scenario we could use scoped and in which transient in controller example for example if we are retrieving few rows from database? I'm trying to understand scoped vs transient usage scenario in this case. – sensei Sep 24 '17 at 19:18
  • 13
    it really depends on the logic you're expecting. For instance, if it's a single db call it actually doesn't make any difference which one you're using. but if you're calling db multiple times in the same request, then you can use scoped lifetime, as it keeps the same repository object in the memory and reuses multiple times within the same Http Request context. Whereas the transient one creates a new repository object multiple times (and consumes more memory). If you explain your specific scenario it'd be easy to judge which one suits better. – akardon Sep 25 '17 at 01:13
  • 14
    One important point to highlight here is Singleton, Scoped and Transient are like russian doills, one within the other. It is not possible to reverse their order when nesting, for eg. a scoped or singleton cannot be contained in a Transient, because we would be extending the lifetime of the parent which goes against containment! – DL Narasimhan Apr 21 '20 at 19:20
  • @akazemis for restful I reckon we can stick with singleton why it has to be a tansient – codebased Mar 26 '21 at 00:00
  • 1
    @DLNarasimhan are you sure about the nesting restriction? It doesn't sound right to me as these types only define rules for creation, not destruction. If I have a Singleton that injects a Transient object inside, it just means that other objects that also want the same Transient object will get their own instance. The Singleton's Transient object won't get destroyed / garbage collected because because the Singleton still holds the reference. This is actually a perfectly valid and common scenario; I don't think there is any Russian Doll restriction here. – ThisGuy Oct 13 '21 at 21:23
  • There is a Russian Doll restriction unfortunately. I tested it out as initially didn't believe it myself thinking there was maybe a proxy that wrapped the transient implementation class. This .NET core documentation makes it clear there is a restriction: https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped and offers a solution to do a transient within a singleton using `IServiceScopeFactory`: https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scope-scenarios . It all seems clumsy to me as now framework classes become a dependency. – GameSalutes Nov 15 '21 at 20:34
  • Honestly for those scenarios where you need Singleton -> Transient you are probably better off implementing a slice of the DI yourself by creating a factory class that handles creating the instances. The factory could depend on `IServiceScopeFactory` or `IServiceProvider` if it is convenient for getting instances of nested dependencies. Then in the original singleton class it can depend on a singleton instance of the factory. – GameSalutes Nov 15 '21 at 20:39
  • @DLNarasimhan "a scoped or singleton cannot be contained in a Transient" - did you mean to say "a transient cannot be contained in a scoped or singleton"? That would make more sense to me. A singleton object will always exist, so I don't see why a reference to it cannot exist in a transient. – David Klempfner May 12 '23 at 01:34
  • https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped says it's fine to resolve a singleton service from a scoped or transient service and to resolve a scoped service from another scoped or transient service. – David Klempfner May 12 '23 at 02:18
471

Which one to use

Transient

  • since they are created every time they will use more memory & Resources and can have a negative impact on performance
  • use this for the lightweight service with little or no state.

Scoped

  • better option when you want to maintain state within a request.

Singleton

  • memory leaks in these services will build up over time.
  • also memory efficient as they are created once reused everywhere.

Use Singletons where you need to maintain application wide state. Application configuration or parameters, Logging Service, caching of data is some of the examples where you can use singletons.

Injecting service with different lifetimes into another

  1. Never inject Scoped & Transient services into Singleton service. ( This effectively converts the transient or scoped service into the singleton.)

  2. Never inject Transient services into scoped service ( This converts the transient service into the scoped.)

Note: I think it's fair to say that the above advice is seriously disputed. Many developers think it's fine to inject, for example, a transient into a singleton.

Jez
  • 27,951
  • 32
  • 136
  • 233
bereket gebredingle
  • 12,064
  • 3
  • 36
  • 47
  • 36
    This is the best answer. I like part where you give examples. It is not so hard to understand how they work. It is way harder to think which service where to put and how and when memory cleaned of them. It would be great if you explain more about that. – valentasm May 23 '20 at 08:38
  • 15
    I don't understand why `Transient` is recommneded for "lightweight service with little or no state". Why not singleton in such case? Wouldn't it be even better to instantiate that small service just once and use it multiple times since it's stateless? Even if the service instantaition is cheap, if you do it a lot of times, the overhead will grow. With singleton, it stays the same – mnj Jul 08 '20 at 08:00
  • 1
    @Loreno In my opinion, the recommendation is **not** if (lightweight and no state) then transient. The recommendation should be if-and-only-if (lightweight and no state) then transient. Thus there is nothing in this comment to argue against a singleton. – H2ONaCl Jan 02 '21 at 05:00
  • 5
    It should be added that when using singletons, you must make sure they are thread-safe since they can be used by multiple concurrent requests running on separate threads. – Eric Mutta Jan 02 '21 at 14:56
  • 12
    What's the issue with injecting a transient service into a scoped service? From my understanding, doing so does not make the transient services _become_ a singleton (if you injected the same transient service somewhere else, it would be a different object), so provided the transient service has no state (which should be implicit), I don't see a problem. – ajbeaven Jan 12 '21 at 03:55
  • @Loreno If i'm understanding everything, i think that transient would allow more memory/resources to be added as the number of users/workload increases. Where Singletons would keep the same memory use for the whole application life. Scoped would use resources on a per task basis, like a meeseeks. I think i got it, but feel free to correct me here. – CloudEmber Jan 20 '21 at 17:23
  • 1
    @S-eagle Exactly my point. If a service has NO STATE, what's the point of creating many instances of it, which uses more and more memory. Singleton will use a constant amount of memory. – mnj Jan 20 '21 at 17:47
  • @Loreno For example, i'm using transient for a DB connection/provider class. It is mostly stateless, but transient allows different users to get their own DB connection and memory to provide for the number of users that i expect to encounter. (Server capacities notwithstanding). I wouldn't want many users to be stuck sharing the same small chunk of memory. – CloudEmber Jan 20 '21 at 18:52
  • 1
    @S-eagle If your service has some internal state, then I fully understand your concern, especially in a multi-threaded scenarios. However, if your service is fully stateless, what is the issue with using the same instance? – mnj Jan 20 '21 at 20:55
  • @Loreno Statelessness is not your only deciding factor. Sometimes it makes sense that each user gets their own instance for performance reasons, even if your class is stateless. Lightweight classes are cheap to instantiate, so that's why Transient might make sense. The answer for your project is that *it depends*, on your tasks and workload. – CloudEmber Jan 22 '21 at 21:20
  • 5
    @S-eagle Could you give an example of such a stateless class that would gain some performance if it is instantiated per request (transient)? I'd really like to gain a good understanding of this. – mnj Jan 23 '21 at 19:44
  • 3
    If you are not supposed to inject transient services into neither scoped nor singleton, then where would you inject your transient services? – Snippy Valson Aug 09 '21 at 12:31
  • Whether a transient service should be injected into a scoped or singleton service seems to depend on the specific transient and whether the longer lifetime and shared use is okay (e.g. whether it's stateful, holds resources that should be disposed promptly, is thread-safe...) – Matt Giles Oct 07 '21 at 17:38
  • 3
    The advice of never "Injecting service with different lifetimes into another" is nonsense. The reason to make an object Transient is because you never want that one object to be shared by multiple objects. There is nothing wrong with using a Transient object inside Singleton or Scoped objects. Suppose you have a cache object, you can have a cache in a global Singleton and another cache in two different Scoped objects. In all of these cases you want each of these objects to have their own cache and the way to accomplish that is to make the cache Transient. – ThisGuy Oct 13 '21 at 21:41
  • @Loreno original answer states transient should be 'used' for lightweight services. Its not 'recommending' or saying its the 'best' option. These lightweight services startup quickly so transient is viable, it would be a disaster to make a heavyweight object transient. The answer also states Singleton is memory efficient (which is your point). My view is that transient is a safe option - you don't have to worry about thread safety issues or memory leaks. If you know your object is 100% safe then go with Singleton to get the (slight) efficiency benefit. But sometimes its better to be cautious. – wilmol Dec 15 '21 at 23:23
  • @CloudEmber could you explain how an 'instance per request' of a stateless class provides a performance gain? My understanding is that since its stateless there's no state (no stateful fields on the heap) to replicate for each request. When the methods are executed concurrently the local variables are already replicated for each request since they're stored on the stack (each thread gets its own stack), hence the name 'local' variable. – wilmol Dec 15 '21 at 23:50
  • @winmol As i've mentioned, _it depends_ on the context of your app and how you're using this state-less class. You should put frequently-used stateless objects in singletons for other transient and scoped task runners/services to use, that should be fine. But if your state-less class is not frequently used, then why reserve the memory to keep holding it while the app scales up on other tasks? .NET gives us the ability to make that decision. – CloudEmber Dec 19 '21 at 22:45
  • 1
    Wondering why there would be build of memory leaks when singleton services are used, can someone throw some light on that? – Carthic Feb 18 '22 at 04:42
  • @ajbeaven Yeah, that's a bit of a strange comment - the lifetime of services consumed by another service does not impact the lifetime of the consuming service... If you have a singleton using services that are transient, the singleton still remains a singleton. – Adriaan Feb 22 '22 at 02:05
  • It is accurate to say singletons are more memory efficient than transient? Singleton is stored in memory the entire lifetime. Transient object is only stored in memory when its needed. – wilmol May 12 '22 at 23:47
  • Transient means it will return a new instance so if it is nested into scoped then why does transient become scoped? – variable Jul 13 '22 at 13:03
160

This image illustrates this concept well. Unfortunately, I could not find the source of this image, but someone made it, he has shown this concept very well in the form of an image. enter image description here

Update: Image reference : ASP.NET Core Service Lifetimes (Infographic) , Author: @WaqasAnwar

Hamed Naeemaei
  • 8,052
  • 3
  • 37
  • 46
  • 31
    Here is the original source of the above image. https://www.ezzylearning.net/tutorial/asp-net-core-service-lifetimes-infographic/ Actually I posted it on my blog 5 days ago :-) – Waqas Anwar Nov 13 '20 at 17:43
  • I read your article and I have a ton of these `services.AddTransient();`. I have a service that has a count of 193 in memory! This service just has stateless methods, should this be scoped instead of transient so I can have only one created for all my controllers? – Mike Flynn May 12 '21 at 21:19
  • @MikeFlynn For one instance per each request you should use `AddScoped();`. but for one instance for all of requests use `AddSingelton();` – Hamed Naeemaei May 15 '21 at 04:43
  • 1
    Wouldn’t the singleton be held in memory until an application restart happens? I don’t want a ton of singletons hanging around. – Mike Flynn May 15 '21 at 11:34
  • Yes remains in memory until the restart – Hamed Naeemaei Jan 26 '23 at 10:35
74

Transient, scoped and singleton define object creation process in ASP.NET MVC core DI(Dependency Injection) when multiple objects of the same type have to be injected. In case you are new to dependency injection you can see this DI IoC video.

You can see the below controller code in which I have requested two instances of "IDal" in the constructor. Transient, Scoped and Singleton define if the same instance will be injected in "_dal" and "_dal1" or different.

public class CustomerController : Controller
{
    IDal dal = null;

    public CustomerController(IDal _dal,
                              IDal _dal1)
    {
        dal = _dal;
        // DI of MVC core
        // inversion of control
    }
}

Transient: In transient, new object instances will be injected in a single request and response. Below is a snapshot image where I displayed GUID values.

Enter image description here

Scoped: In scoped, the same object instance will be injected in a single request and response.

Enter image description here

Singleton: In singleton, the same object will be injected across all requests and responses. In this case one global instance of the object will be created.

Below is a simple diagram which explains the above fundamental visually.

enter image description here

The above image was drawn by the SBSS team when I was taking ASP.NET MVC training in Mumbai. A big thanks goes to the SBSS team for creating the above image.

Dhana
  • 1,618
  • 4
  • 23
  • 39
Shivprasad Koirala
  • 27,644
  • 7
  • 84
  • 73
  • 25
    This is the single most complicated explanation of a transient service I've ever seen. Transient = Any time this service is resolved is the equivalent of assigning your variable `new TService`. Scoped will cache the first initialisation of it for that "scope" (http request in most cases). Singleton will cache only one instance ever for the lifetime of the application, Simple as that. The above diagrams are so convoluted. – Mardoxx Mar 02 '18 at 16:28
  • 5
    So sorry i thought i will make it more simpler with diagrams and code snapshot :-) But i do get your point. – Shivprasad Koirala Mar 04 '18 at 02:14
  • 1
    I found this helpful in the unique case where you have multiple instances injected, and Transient registration is used. Thanks – Stokely Nov 16 '20 at 20:27
  • What scope does the controller itself have? – variable Jul 13 '22 at 13:04
  • It's the best explanation I've vere seen. Thanks for clarification – mustafa00 Jan 13 '23 at 11:29
  • @Mardoxx what do you mean by "http request in most cases"? What other cases are there? – David Klempfner May 12 '23 at 01:44
  • 1
    @DavidKlempfner Anywhere where you need to have new instances created per "request"; whatever a request is. E.g. a worker which listens for files (meaning any datastream) to process. You could have a scoped container for each processing task and resolve from that, rather than chaining dependencies along or making all your instances singletons. – Mardoxx May 12 '23 at 23:06
68

AddSingleton()

AddSingleton() creates a single instance of the service when it is first requested and reuses that same instance in all the places where that service is needed.

AddScoped()

In a scoped service, with every HTTP request, we get a new instance. However, within the same HTTP request, if the service is required in multiple places, like in the view and in the controller, then the same instance is provided for the entire scope of that HTTP request. But every new HTTP request will get a new instance of the service.

AddTransient()

With a transient service, a new instance is provided every time a service instance is requested whether it is in the scope of the same HTTP request or across different HTTP requests.

Community
  • 1
  • 1
Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
  • Does scoped lifetime have any meaning outside of ASP.NET, for example in a .NET 6 worker service application? – C-F Aug 03 '22 at 22:02
  • @C-F, It does have meaning, but you create your scopes by yourself when you need it. See https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scope-scenarios – C-F Aug 03 '22 at 22:08
  • 1
    @Yasser, Out of all explanation, your explanation is more clear. Thank you. – JackFrost Oct 04 '22 at 09:13
59
  • Singleton is a single instance for the lifetime of the application domain.
  • Scoped is a single instance for the duration of the scoped request, which means per HTTP request in ASP.NET.
  • Transient is a single instance per code request.

Normally the code request should be made through a constructor parameter, as in

public MyConsumingClass(IDependency dependency)

I wanted to point out in @akazemis's answer that "services" in the context of DI does not imply RESTful services; services are implementations of dependencies that provide functionality.

user1969177
  • 786
  • 5
  • 8
38

After looking for an answer for this question I found a brilliant explanation with an example that I would like to share with you.

You can watch a video that demonstrate the differences HERE

In this example we have this given code:

public interface IEmployeeRepository
{
    IEnumerable<Employee> GetAllEmployees();
    Employee Add(Employee employee);
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MockEmployeeRepository : IEmployeeRepository
{
    private List<Employee> _employeeList;

    public MockEmployeeRepository()
    {
        _employeeList = new List<Employee>()
    {
        new Employee() { Id = 1, Name = "Mary" },
        new Employee() { Id = 2, Name = "John" },
        new Employee() { Id = 3, Name = "Sam" },
    };
    }

    public Employee Add(Employee employee)
    {
        employee.Id = _employeeList.Max(e => e.Id) + 1;
        _employeeList.Add(employee);
        return employee;
    }

    public IEnumerable<Employee> GetAllEmployees()
    {
        return _employeeList;
    }
}

HomeController

public class HomeController : Controller
{
    private IEmployeeRepository _employeeRepository;

    public HomeController(IEmployeeRepository employeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    [HttpGet]
    public ViewResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create(Employee employee)
    {
        if (ModelState.IsValid)
        {
            Employee newEmployee = _employeeRepository.Add(employee);
        }

        return View();
    }
}

Create View

@model Employee
@inject IEmployeeRepository empRepository

<form asp-controller="home" asp-action="create" method="post">
    <div>
        <label asp-for="Name"></label>
        <div>
            <input asp-for="Name">
        </div>
    </div>

    <div>
        <button type="submit">Create</button>
    </div>

    <div>
        Total Employees Count = @empRepository.GetAllEmployees().Count().ToString()
    </div>
</form>

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddSingleton<IEmployeeRepository, MockEmployeeRepository>();
}

Copy-paste this code and press on the create button in the view and switch between AddSingleton , AddScoped and AddTransient you will get each time a different result that will might help you understand this.

AddSingleton() - As the name implies, AddSingleton() method creates a Singleton service. A Singleton service is created when it is first requested. This same instance is then used by all the subsequent requests. So in general, a Singleton service is created only one time per application and that single instance is used throughout the application life time.

AddTransient() - This method creates a Transient service. A new instance of a Transient service is created each time it is requested.

AddScoped() - This method creates a Scoped service. A new instance of a Scoped service is created once per request within the scope. For example, in a web application it creates 1 instance per each http request but uses the same instance in the other calls within that same web request.

Offir
  • 3,252
  • 3
  • 41
  • 73
13

DI containers can be pretty mystifying at first, especially with regard to lifetimes. After all, containers use reflection to make everything "just work." It helps to think about what containers are actually accomplishing for you under the hood: composing object graphs.

For a .NET web app, the alternative to using a DI container is to replace the default controller activator with your own, which must manage lifetimes and construct dependency graphs manually. For learning purposes, pretend you have a controller activator that is hard-coded to return one particular controller each time there is a web request:

// This class is created once per application during startup.  In DI terms, it is the
// "composition root."
public class DumbControllerActivator
{
    // Shared among all consumers from all requests
    private static readonly Singleton1 singleton1 = new Singleton1();
    private static readonly Singleton2 singleton2 = new Singleton2();

    // This method's responsibility is to construct a FooController and its dependecies.
    public FooController HandleFooRequest()
    {
        // Shared among all consumers in this request
        var scoped1 = new Scoped1();
        var scoped2 = new Scoped2(singleton1, scoped1);

        return new FooController(
            singleton1,
            scoped1,
            new Transient1(                     // Fresh instance
                singleton2,
                new Transient2(scoped2)),       // Fresh instance
            new Transient3(                     // Fresh instance
                singleton1,
                scoped1,
                new Transient1(                 // Fresh instance
                    singleton2,
                    new Transient2(scoped2)));  // Fresh instance
    }
}
  • The activator creates each singleton instance only once and then holds onto it throughout the lifetime of the application. Each consumer shares that single instance (even consumers from separate requests).
  • For scoped dependencies, the activator creates one instance per web request. Within that request, every consumer shares that single instance, but from request to request, the instances are different.
  • For transient dependencies, each consumer gets its own private instance. There is no sharing at all.

For a much deeper dive into DI, I highly recommend the book Dependency Injection Principles, Practices, and Patterns. My answer is basically just repeating what I learned there.

codevision
  • 5,165
  • 38
  • 50
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
6

I like to draw what I understand.

enter image description here

enter image description here

enter image description here

4

Adding to all the fantastic answers here. This answer will explain the different lifetime and when it is appropriate to choose them.As with many things in software development, there are no absolutes and many condition can affect the best choices, so please treat this answer as a general guidance.

ASP.NET Core ships with its own built-in Dependency Injection Container which it uses to resolve the services it needs during the request lifecycle. All framework services, for example, like Logging, configuration, Routing etc.., use dependency injection and they are registered with the Dependency Injection Container when the application host is built. Internally, the ASP.NET Core framework provides the dependencies required when activating framework components, such as Controllers and Razor pages.

A Dependency Injection Container, sometimes referred to as an Inversion of Control, or IoC Container, is a software component that manages the instantiation and configuration of objects. The dependency Injection Container is not a requirement to apply the dependency injection pattern, but as your application grows using one can greatly simply the management of dependencies, including their lifetimes. Services are registered with the container at start up and resolved from the container at runtime whenever they are required. The container is responsible for creating and disposing of instances of required services, maintaining them for a specified lifetime.

There are two primary interfaces that we code against when using the Microsoft Dependency Injection Container.

  1. The IServiceCollection interface defines a contract for registering and configuring a collection of service descriptors. From the IServiceCollection we build an IServiceProvider.
  2. IServiceProvider, defines a mechanism for resolving a service at runtime.

When registering a service with the container, a service lifetime should be chosen for the service. The service lifetime controls how long a resolved object will live for after the container has created it. The lifetime can be defined by using the appropriate extension method on the IServiceCollection when registering the service. There are three lifetimes available for use with the Microsoft Dependency Injection Container.

  1. Transient
  2. Singleton
  3. Scoped

The Dependency Injection Container keeps track of all the instances of services it creates, and they are disposed of or released for garbage collection once their lifetime has ended. The chosen lifetime affects whether the same service instance may be resolved and injected into more than one dependent consumer. It is crucial, therefore, to choose the lifetime of services very wisely.

Transient Services

When a service is registered as Transient, a new instance of that service is created and returned by the container every time the service is resolved. In other words, every dependent class that accepts a Transient service via injection from the container will receive its own unique instance. Because each dependent class receives its own instance, methods on the instance are safe to mutate internal state without fear of access by other consumers and threads.

Uses/ Characteristics of Transient Services

  1. Transient service is most useful when the service contains mutable state and is not considered thread safe.
  2. Using Transient services can come with a small performance cost, although that cost may be negligible for applications under general load.
  3. Whenever an instance is resolved, which could be as often as every request, memory for that instance must be allocated. This leads to additional work for the garbage collector in order to clean up those short lived objects.
  4. Transient service are easiest to reason about because instances are never shared; therefore they tend to be safest choice when it is unclear which lifetime is going to be the best option when you register a service.

Singleton Services

An application service registered with the singleton lifetime will be created only once during the lifetime of the Dependency Injection Container. In ASP.NET Core, this is equivalent to the lifetime of the while we application. The first time the service is required, the container will create an instance. After that, the same instance can be reused and injected into all dependent classes. The instance will remain reachable for the lifetime of the container, so it does not require disposal or garbage collection.

Uses/ Characteristics of Singleton Service

  1. Suppose the service is required frequently, such as per request. This can improve application performance by avoiding repeated allocation of new objects, each of which may require garbage collection only moments later.
  2. Additionally, if the object is expensive to construct, limiting this to a single instance can improve your application performance, as it only takes place one time when the service is first used.
  3. When choosing to register a service with Singleton lifetime, you must consider thread safety. Because the same instance of a Singleton service can be used by multiple requests concurrently. Any mutable state without suitable locking mechanism could lead to unexpected behaviour.
  4. Singleton are very well suited to functional-style services, where the methods take an input and return an output, with no shared state being used.
  5. A reasonable use case for Singleton lifetime in ASP.NET Core is for the memory cache, where the state must be shared for the cache to function.
  6. When registering services for Singleton, consider the implications of a single instance remaining allocated for the life of the application. a. It is possible to create memory leaks is the instance holds large amount of memory b. This can be especially problematic if memory usage can grow during the instance’s lifetime, given that it will never be released for garbage collection. c. If a service has high memory requirements but is used very infrequently, then the singleton lifetime may not be the most appropriate choice.

Scoped Services

Scoped services sit in a middle ground between Transient and Singleton. An Instance of the scoped service lives for the length of the scope from which it is resolved. InASP.NET Core, a scope is created within your application for each request it handles. Any scope services will be created once per scope, so that they act similarly to singleton services, but within the context of the scope. All framework components such as middleware and MVC controllers get the same instance of a scoped service when handling a particular request.

Uses/ Characteristics of Scoped Services

  1. The container creates a new instance per request.
  2. Because the container resolves a new instance of the type per request, it is not generally required to be thread safe.
  3. Components within the request life cycle are invoked in sequence, so the shared instance is not used concurrently.
  4. Scoped services are useful if multiple consumers may require the same dependency during a request. a. An excellent example of such a case is when using Entity Framework Core. By default, the DbContext is registered with the scoped lifetime. Change tracking, therefore, applies across the entire request. Multiple components can make changes to the shared DbContext.

Avoiding Captive Dependencies

When registering your dependencies, it is crucial to ensure that the chosen lifetime is appropriate considering any dependencies that the service has of its own. This is necessary to avoid something called captive dependencies, which is where a service may live longer than is intended.

The thumb rule is, a service should not depend on a service with a lifetime shorter than its own. For example, a service registered with the singleton lifetime should not depend on a transient service. Doing so would result in the transient service being captured by the singleton service, with the instance unintentionally being referenced for the life of the application. This can lead to problematic and sometime hard to track down runtime bugs and behaviours, such as accidentally sharing non-thread safe services between threads or allowing objects to live past their intended lifetime. To visualize this, let us consider which lifetimes can safely depend on services using another lifetime.

  1. Since it will be a short-lived service, a transient service can safely depend on service that have transient, scoped or singleton lifetime
  2. Scope services are little tricky. If they depend on a transient service, then a single instance of that transient service will live for the life of the scope across the whole request. You may or may not want this behaviour. To be absolutely safe, you might choose not to depend on transient services from scoped services, but it is safe for a scoped service to depend on other scoped or singleton services.
  3. A singleton service is most restrictive in terms of its dependencies, and it should not depend on transient or scoped services, but can depend upon other singleton services. Capture of scope services by singletons is one of the more dangerous possibilities. Because scoped services could be disposed of then the scope ends, It is possible that the singleton may try and access them after their disposal. This could lead to runtime exceptions in production, a really bad situation.

enter image description here

j4jada
  • 334
  • 1
  • 9
2

Probably the best illustration of the lifetime comes into play with the EntityFramework/Core via DbContext.

It is recommended that DbContext and repositories that interact with DbContext should be wired up with a Scoped lifetime because a DbContext is obviously a stateful construct. So you'd not want to use a Singleton because you'd end up with all kinds of concurrency issues. You'd not want to use Transient because DbContext is not thread safe. Remember, Transient is for use cases where you're dealing with stateless objects/classes.

And since most repositories are called by controllers, it really makes sense to use a Scoped lifetime. It's conceivable that a DbContext could be invoked multiple times during a single action method as part of a transation.

This article doesn't speak directly about these lifetimes but gives a great explanation for why a Scoped lifetime is most appropriate for DbContext.

https://mehdi.me/ambient-dbcontext-in-ef6/?msclkid=00251b05d01411ec8d85d232374f26d5

Charles Owen
  • 2,403
  • 1
  • 14
  • 25