ChatGPT解决这个技术问题 Extra ChatGPT

AddTransient, AddScoped and AddSingleton Services Differences

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>();
}
@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.
@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, that is why I asked this question.
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.
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.

D
David Pine

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:

https://i.stack.imgur.com/ilUB6.png

https://i.stack.imgur.com/X2QCd.png

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)


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.
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.
Could you also explain the common pitfalls in which we nest transient or scoped dependencies in a singleton?
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.
@akazemis doing the Lord's work here...clear and concise explanation and graphics w/ guid examples drive the illustration home. Thank you!
C
Crypth

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


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.
You're right. Transient is not the most lightweight, I just said it's suitable for lightweight RESTful services :)
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.
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.
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!
b
bereket gebredingle

Which one to use

Transient

since they are created every time they will use more memory & Resources and can have the 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

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


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.
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
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.
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.
@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.
H
Hamed Naeemaei

https://i.stack.imgur.com/M0le5.jpg

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


Here is the original source of the above image. ezzylearning.net/tutorial/… Actually I posted it on my blog 5 days ago :-)
I read your article and I have a ton of these services.AddTransient<IProductService, ProductService>();. 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?
@MikeFlynn For one instance per each request you should use AddScoped<IProductService, ProductService>();. but for one instance for all of requests use AddSingelton<IProductService, ProductService>();
Wouldn’t the singleton be held in memory until an application restart happens? I don’t want a ton of singletons hanging around.
D
Dhana

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.

https://i.stack.imgur.com/Xszmo.png

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

https://i.stack.imgur.com/pSm3C.png

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.

https://i.stack.imgur.com/2b9Lq.png

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.


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.
So sorry i thought i will make it more simpler with diagrams and code snapshot :-) But i do get your point.
I found this helpful in the unique case where you have multiple instances injected, and Transient registration is used. Thanks
What scope does the controller itself have?
C
Community

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.


u
user1969177

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.


O
Offir

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.


Plus 1 for giving credits to the author. :)
I have a feeling no one used my code and everyone went ahead for the attached video :)
c
codevision

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.


İ started to read that book. Thank you for your suggestions.
R
Raihan Mahmud RAM

Transient: A new instance is provided every time an the instance is requested whether it is in the scope of same http request or across different http requests.

Scoped: We get the same instance within the scope of a given http request but a new instance across different http requests.

Singleton: There is only a single instance. An instance is created, when service is first requested and that single instance single instance will be used by all subsequent http request throughout the application.


C
Charles Owen

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