web-dev-qa-db-fra.com

AddTransient, AddScoped et AddSingleton Services Differences?

Je veux implémenter dependency injection dans Asp.Net Core. Ainsi, après l’ajout de ces codes à la méthode ConfigureServices, les deux méthodes fonctionnent.

Quelle est la différence entre services.AddTransient et service.AddScoped les méthodes sont _Asp.Net Core_?

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

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddScoped<IEmailSender, AuthMessageSender>();
}
_
641
Elvin Mammadov

TL; DR

Les objets transitoires sont toujours différents. une nouvelle instance est fournie à chaque contrôleur et à chaque service.

Les objets étendus sont les mêmes dans une demande, mais diffèrent d'une requête à l'autre.

Les objets Singleton sont les mêmes pour chaque objet et chaque demande.

Pour plus de précisions, cet exemple de asp.net docs montre la différence:

Pour démontrer la différence entre ces options de durée de vie et d’enregistrement, considérons une interface simple représentant une ou plusieurs tâches comme une opération portant un identificateur unique, OperationId. Selon la manière dont nous configurons la durée de vie de ce service, le conteneur fournira la même instance ou des instances différentes du service à la classe demandeuse. Pour préciser la durée de vie demandée, nous allons créer une option de type par durée de vie:

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
    {
    }
}

Nous implémentons ces interfaces en utilisant une seule classe, Operation, qui accepte un Guid dans son constructeur ou utilise un nouveau Guid si aucun n'est fourni.

Ensuite, dans ConfigureServices, chaque type est ajouté au conteneur en fonction de sa durée de vie nommée:

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

Notez que le service IOperationSingletonInstance utilise une instance spécifique avec un ID connu de Guid.Empty, de sorte qu'il sera clairement indiqué lorsque ce type est utilisé. Nous avons également enregistré une OperationService qui dépend de chacun des autres types Operation, de sorte qu'il soit clairement indiqué dans une demande si ce service reçoit la même instance que le contrôleur, ou une nouvelle, pour chaque type d'opération. Ce service ne fait qu'exposer ses dépendances en tant que propriétés, de sorte qu'elles puissent être affichées dans la vue.

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;
        }
    }
}

Pour illustrer les durées de vie des objets dans et entre des requêtes individuelles distinctes adressées à l'application, l'exemple inclut un OperationsController qui demande chaque type de type IOperation ainsi qu'un OperationService. L’action Index affiche ensuite toutes les valeurs OperationId du contrôleur et du service.

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();
        }
    }
}

Deux requêtes distinctes sont à présent adressées à cette action du contrôleur: First Request

Second Request

Observez laquelle des valeurs OperationId varie au sein d'une requête et entre les requêtes.

  • Les objets transitoires sont toujours différents. une nouvelle instance est fournie à chaque contrôleur et à chaque service.

  • Les objets scopés sont les mêmes dans une requête, mais différents d'une requête à l'autre

  • Les objets singleton sont les mêmes pour chaque objet et chaque demande (que l'instance soit fournie ou non dans ConfigureServices)

1168
akazemis

Dans l'injection de dépendance de dotnet, il y a 3 durées de vie principales:

Singleton qui crée une instance unique dans toute l'application. Il crée l'instance pour la première fois et réutilise le même objet dans tous les appels.

Scoped Les services à vie sont créés une fois par demande dans l'étendue. C'est l'équivalent de Singleton dans la portée actuelle. par exemple. dans MVC, il crée une instance pour chaque requête http, mais utilise la même instance dans les autres appels de la même requête Web.

Transitoire Des services à vie sont créés chaque fois qu'ils sont demandés. Cette vie fonctionne mieux pour les services légers et sans état.

Ici vous pouvez trouver et des exemples pour voir la différence:

http://dotnetliberty.com/index.php/2015/10/15/asp-net-5-mvc6-dependency-injection-in-6-steps/

https://codewala.net/2015/04/30/your-dependency-injection-ready-asp-net-asp-net-5/

et voici le lien vers la documentation officielle:

https://docs.asp.net/fr/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options

270
akazemis
  • Singleton est une instance unique pour la durée de vie du domaine d'application.
  • Scoped est une instance unique pour la durée de la demande scoped, ce qui signifie une requête HTTP dans ASP.NET.
  • Le transitoire est une instance unique par code demande.

Normalement, la demande de code doit être effectuée via un paramètre constructeur, comme dans

public MyConsumingClass(IDependency dependency)

Je voulais souligner dans la réponse de @ akazemis que "services" dans le contexte de DI n'implique pas des services RESTful; les services sont des implémentations de dépendances fournissant des fonctionnalités.

24
user1969177

Transient, scoped et singleton définissent le processus de création d’objet dans l’ID principal ASP.NET MVC lorsque plusieurs objets du même type doivent être injectés. Si vous débutez l’injection de dépendance, vous pouvez voir ceci DI IOC video

Vous pouvez voir le code de contrôleur ci-dessous dans lequel j'ai demandé deux instances de "IDal" dans le constructeur. Transient, Scoped et Singleton définissent si la même instance sera injectée dans "_dal" et "_dal1" ou différents.

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

Transitoire: - Dans les transitoires, de nouvelles instances d'objet seront injectées dans une requête et une réponse uniques. Ci-dessous, une image instantanée où j’ai affiché les valeurs GUID.

enter image description here

Scoped: - Dans Scoped, la même instance d'objet sera injectée dans une requête et une réponse uniques.

enter image description here

Singleton: - Dans Singleton, le même objet sera injecté dans toutes les demandes et réponses. Dans ce cas, une instance globale de l'objet sera créée.

Vous trouverez ci-dessous un schéma simple qui explique visuellement les fondamentaux ci-dessus.

MVC DI image

L'image ci-dessus a été dessinée par l'équipe SBSS lorsque je suivais la formation formation ASP.NET MVC à mumbai , un grand merci à l'équipe SBSS pour créer l'image ci-dessus.

24

AddSingleton ()

AddSingleton () crée une seule instance du service lors de la première demande et réutilise cette même instance à tous les endroits où ce service est requis.

AddScoped ()

Dans le service de portée avec chaque demande http, nous obtenons une nouvelle instance. Cependant, avec dans la même demande http si le service est requis à plusieurs endroits, comme dans la vue et dans le contrôleur, la même instance est fournie pour toute la portée de cette demande http. Mais chaque nouvelle requête http obtiendra une nouvelle instance du service.

AddTransient ()

Avec un service transitoire, une nouvelle instance est fournie chaque fois qu'une demande de service est demandée, qu'elle soit dans la portée de la même demande http ou à travers différentes demandes http.

3
Yasser

Après avoir cherché une réponse à cette question, j'ai trouvé une explication brillante avec un exemple que j'aimerais partager avec vous.

Vous pouvez regarder une vidéo qui montre les différences ICI

Dans cet exemple, nous avons ce code donné:

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();
    }
}

Créer une vue

@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>();
}

Copier-coller ce code et appuyer sur le bouton de création de la vue et basculer entre AddSingleton, AddScoped et AddTransient, vous obtiendrez à chaque fois un résultat différent qui pourrait vous aider à comprendre cette explication. :

AddSingleton () - Comme son nom l'indique, la méthode AddSingleton () crée un service Singleton. Un service Singleton est créé lors de la première demande. Cette même instance est ensuite utilisée par toutes les requêtes suivantes. Ainsi, en général, un service Singleton est créé une seule fois par application et cette instance unique est utilisée pendant toute la durée de vie de l'application.

AddTransient () - Cette méthode crée un service transitoire. Une nouvelle instance d'un service transitoire est créée à chaque demande.

AddScoped () - Cette méthode crée un service Scoped. Une nouvelle instance d'un service Scoped est créée une fois par demande dans l'étendue. Par exemple, dans une application Web, il crée une instance pour chaque demande http mais utilise la même instance dans les autres appels de cette même demande Web.

2
Offir Pe'er

Comme décrit ici (ce lien est très utile) avec un exemple,

Ce mappage entre l'interface et le type concret définit que chaque fois que vous demandez un type de IContryService, vous obtenez une nouvelle instance de CountryService. C'est ce que signifie transitoire dans ce cas. Vous pouvez également ajouter des mappages de singleton (avec AddSingleton) et des mappages étendus (avec AddScoped). Scoped dans ce cas signifie scoped à une requête HTTP, ce qui signifie également qu'il s'agit d'un singleton pendant l'exécution de la requête en cours. Vous pouvez également ajouter une instance existante au conteneur DI à l'aide de la méthode AddInstance. Ce sont les moyens presque complets pour vous inscrire à IServiceCollection

1
Rehmanali Momin