1773 votes

Ajouter Transitoire, Ajouter Encadrée et Ajouter Singleton Services Différences

Je veux implémenter l'injection de dépendances (DI) dans ASP.NET Core. Donc après avoir ajouté ce code à la méthode ConfigureServices, les deux méthodes fonctionnent.

Quelle est la différence entre les méthodes services.AddTransient et services.AddScoped dans ASP.NET Core?

public void ConfigureServices(IServiceCollection services)
{
    // Ajouter des services de framework.

    // Ajouter des services d'application.
    services.AddTransient();
    services.AddScoped();
}

191 votes

@tmg Les documents disent que les "services à durée de vie transitoire sont créés à chaque fois qu'ils sont demandés." et que les "services à durée de vie de portée sont créés une fois par demande.", ce qui, sauf si ma compréhension de l'anglais est plus faible que je ne le pensais, signifie en fait exactement la même chose.

3 votes

@Neutrino Cela ne fait pas. Vérifiez la réponse ci-dessous

111 votes

@tmg Je sais. Je souligne simplement que les documents ne sont pas du tout clairs sur ce point, donc renvoyer les gens vers les documents n'est pas très utile.

2892voto

akazemis Points 1

TL;DR

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

Les objets à portée sont les mêmes pour une requête, mais différents entre différentes requêtes.

Les objets singleton sont les mêmes pour chaque objet et chaque requête.

Pour plus de clarification, cet exemple de la documentation .NET montre la différence :

Pour démontrer la différence entre ces options de durée de vie et d'enregistrement, considérez une interface simple qui représente une ou plusieurs tâches comme une opération avec un identifiant unique, OperationId. En fonction de la façon dont nous configurons la durée de vie de ce service, le conteneur fournira soit la même instance, soit des instances différentes du service à la classe demandée. Pour préciser quelle durée de vie est demandée, nous créerons un type par option de 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 mettons en œuvre ces interfaces à l'aide d'une seule classe, Operation, qui accepte un GUID dans son constructeur, ou utilise un nouveau GUID s'il n'y en a pas:

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

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

services.AddTransient();
services.AddScoped();
services.AddSingleton();
services.AddSingleton(new Operation(Guid.Empty));
services.AddTransient();

Remarquez que le service IOperationSingletonInstance utilise une instance spécifique avec un ID connu de Guid.Empty, donc il sera clair quand ce type est utilisé. Nous avons également enregistré un OperationService qui dépend de chacun des autres types Operation, de sorte qu'il sera clair dans une requête si ce service obtient la même instance que le contrôleur, ou une nouvelle, pour chaque type d'opération. Tout ce que ce service fait, c'est exposer ses dépendances en tant que propriétés, afin 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 démontrer les durées de vie des objets au sein et entre des requêtes individuelles distinctes à l'application, l'exemple comprend un OperationsController qui demande chaque type de IOperation ainsi qu'un OperationService. L'action Index affiche ensuite toutes les valeurs de 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 contient les services demandés par le contrôleur
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // Le service Operation a ses propres services demandés
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

Maintenant, deux requêtes distinctes sont effectuées vers cette action du contrôleur :

Première demande

Deuxième demande

Observez quelles valeurs de OperationId varient dans 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 à portée sont les mêmes pour une requête, mais différents entre différentes requêtes

  • Les objets singleton sont les mêmes pour chaque objet et chaque requête (peu importe si une instance est fournie dans ConfigureServices)

85 votes

J'ai compris les fonctions de chacun d'eux, mais est-ce que quelqu'un pourrait expliquer l'impact de l'utilisation de l'un plutôt que de l'autre. Quels problèmes cela pourrait-il causer si ce n'est pas utilisé correctement ou si on en choisit un plutôt qu'un autre.

16 votes

Dites que vous créez un objet lié au contexte de la requête (comme l'utilisateur actuel) avec une portée singleton, alors il restera la même instance à travers toutes les requêtes http, ce qui n'est pas souhaitable. L'IOC consiste à créer des instances, nous devons donc spécifier la portée de l'instance créée.

4 votes

Pouvez-vous également expliquer les pièges courants dans lesquels nous imbriquons des dépendances transitoires ou à portée limitée dans un singleton ?

489voto

akazemis Points 1

Dans l'injection de dépendance de .NET, il y a trois durées de vie principales :

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

Scopé Les services à durée de vie sont créés une fois par requête dans la portée. Il est équivalent à un singleton dans la portée actuelle. Par exemple, en MVC, il crée une seule instance pour chaque requête HTTP, mais utilise la même instance dans les autres appels à l'intérieur de la même requête web.

Transient Les services à durée de vie sont créés à chaque fois qu'ils sont demandés. Cette durée de vie fonctionne mieux pour les services légers et sans état.

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

Injection de dépendance ASP.NET 5 MVC6 en 6 étapes (lien d'archive web en raison d'un lien mort)

Votre ASP.NET prêt pour l'injection de dépendance : ASP.NET 5

Et voici le lien vers la documentation officielle :

Injection de dépendance dans ASP.NET Core

37 votes

Pouvez-vous s'il vous plaît expliquer pourquoi le Transient est le plus léger? Je pensais que le Transient était le plus lourd à cause du besoin de créer une instance à chaque fois pour chaque injection.

29 votes

Tu as raison. Transient n'est pas le plus léger, j'ai juste dit qu'il convient aux services REST légers :)

4 votes

Donc dans quel scénario pourrions-nous utiliser le 'scoped' et dans quel 'transient' dans l'exemple du contrôleur par exemple si nous récupérons quelques lignes de la base de données? J'essaie de comprendre le scénario d'utilisation de 'scoped' par rapport à 'transient' dans ce cas.

70voto

Shivprasad Koirala Points 1327

Transient, scoped et singleton définissent le processus de création d'objets dans ASP.NET MVC core DI (Injection de dépendance) lorsque plusieurs objets du même type doivent être injectés. Si vous êtes nouveau dans l'injection de dépendances, vous pouvez consulter cette vidéo DI IoC.

Vous pouvez voir ci-dessous le code du contrôleur 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 non.

public class CustomerController : Controller
{
    IDal dal = null;

    public CustomerController(IDal _dal,
                              IDal _dal1)
    {
        dal = _dal;
        // DI de MVC core
        // inversion de contrôle
    }
}

Transient: En transient, de nouvelles instances d'objets seront injectées dans une seule requête et réponse. Ci-dessous, une capture d'écran où j'ai affiché des valeurs GUID.

Saisissez ici la description de l'image

Scoped: En scoped, la même instance d'objet sera injectée dans une seule requête et réponse.

Saisissez ici la description de l'image

Singleton: En singleton, le même objet sera injecté à travers toutes les requêtes et réponses. Dans ce cas, une instance globale de l'objet sera créée.

Voici un schéma simple qui explique visuellement les concepts mentionnés ci-dessus.

entrez la description de l'image ici

L'image ci-dessus a été dessinée par l'équipe SBSS lorsque je suivais une formation ASP.NET MVC à Mumbai. Un grand merci à l'équipe SBSS pour avoir créé l'image ci-dessus.

24 votes

Ceci est l'explication la plus compliquée d'un service temporaire que j'aie jamais vue. Temporaire = Chaque fois que ce service est résolu équivaut à attribuer votre variable new TService. Scoped mettra en cache la première initialisation pour ce "scope" (requête http dans la plupart des cas). Singleton mettra en cache une seule instance pour toute la durée de l'application, aussi simple que cela. Les schémas ci-dessus sont si embrouillés.

5 votes

Je suis vraiment désolé je pensais que je rendrais les choses plus simples avec des diagrammes et des captures de code :-) Mais je comprends ce que tu veux dire.

1 votes

J'ai trouvé cela utile dans le cas unique où vous avez plusieurs instances injectées et l'inscription Transient est utilisée. Merci

49voto

user1969177 Points 161
  • Singleton est une seule instance pour la durée de la domaine d'application.
  • Scoped est une seule instance pour la durée de la requête scoped, ce qui signifie par requête HTTP dans ASP.NET.
  • Transient est une seule instance par requête code.

Normalement, la requête de code devrait être effectuée via un paramètre de constructeur, comme dans

public MyConsumingClass(IDependency dependency)

Je voulais souligner dans la réponse de @akazemis que "services" dans le contexte de l'Injection de Dépendances n'implique pas des services RESTful ; les services sont des implémentations des dépendances qui fournissent une fonctionnalité.

0voto

A. Morel Points 1115

Ceci est les différents types de durées de vie que vous pouvez utiliser dans votre application.

Durée de vie Transient:

Ajouter un service transient signifie qu'à chaque fois que le service est demandé, une nouvelle instance est créée.

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient();

    var fournisseurService = services.BuildServiceProvider();

    var instanceUn = fournisseurService.GetService();
    var instanceDeux = fournisseurService.GetService();

    Debug.Assert(instanceUn != instanceDeux);
}   

Durée de vie Singleton:

Un singleton est une instance qui durera toute la durée de vie de l'application. En termes web, cela signifie qu'après la première demande du service, chaque demande ultérieure utilisera la même instance. Cela signifie également qu'il s'étend sur les requêtes web (donc si deux utilisateurs différents accèdent à votre site web, le code utilise toujours la même instance). La manière la plus simple de penser à un singleton est si vous aviez une variable statique dans une classe, c'est une seule valeur à travers plusieurs instances.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton();

    var fournisseurService = services.BuildServiceProvider();

    var instanceUn = fournisseurService.GetService();
    var instanceDeux = fournisseurService.GetService();

    Debug.Assert(instanceUn == instanceDeux);
}

Durée de vie Scoped

Les objets en durée de vie scoped sont souvent simplifiés en "une instance par requête web", mais c'est en réalité beaucoup plus nuancé que cela. La durée de vie scoped signifie en fait que dans un "scope" créé, les objets seront la même instance. Il se trouve simplement que dans .net core, il enveloppe une requête dans un "scope", mais vous pouvez en fait créer manuellement des scopes.

Par exemple :

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped();

    var fournisseurService = services.BuildServiceProvider();

    var serviceScopeFactory = fournisseurService.GetRequiredService();

    IMyScopedService scopedUn;
    IMyScopedService scopedDeux;

    using (var scope = serviceScopeFactory.CreateScope())
    {
        scopedUn = scope.ServiceProvider.GetService();
    }

    using (var scope = serviceScopeFactory.CreateScope())
    {
        scopedDeux = scope.ServiceProvider.GetService();
    }

    Debug.Assert(scopedUn != scopedDeux);
}

Plus d'informations ici

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X