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 :
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
)
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.
30 votes
@Neutrino, c'est pourquoi j'ai posé cette question.
11 votes
En retard à la fête, je lis les commentaires encore plus tard, mais j'ai imprimé cet article, je l'ai lu, et j'ai noté la même observation dans la marge que je vois maintenant @Neutrino faire ici. L'article était ENTIEREMENT vague en offrant cette analyse. L'exemple, heureusement, était moins confus.
2 votes
@Neutrino je suis probablement en retard pour commenter. mais l'Avertissement est une différence importante entre transient et scoped. parce que je me suis cogné la tête pendant un moment avant de trouver ce post et puis l'article :) AVERTISSEMENT : Lorsque vous utilisez un service scénarisé dans un middleware, injectez le service dans la méthode Invoke ou InvokeAsync. Ne pas injecter via l'injection de constructeur car cela oblige le service à se comporter comme un singleton. Pour plus d'informations,
1 votes
@MandarJogalekar, merci pour votre commentaire. Pourriez-vous s'il vous plaît partager quelques ressources concernant ce que vous avez dit?
1 votes
@ElvinMammadov c'est ce lien docs.asp.net/en/latest/fundamentals/…
76 votes
Tel que je le comprends : Les services à durée de vie transitoire sont créés à chaque fois qu'ils sont demandés. Le mot demandé ici a le sens quotidien en anglais de demander quelque chose, dans ce cas un service. Alors que le mot demande dans une fois par demande se réfère à une requête HTTP. Mais je comprends la confusion.
2 votes
Voici un exemple d'échantillon pour comprendre le comportement : tektutorialshub.com/asp-net-core/…
1 votes
Il s'agit d'une nouvelle instance à chaque fois mais peut causer des fuites de mémoire: docs.microsoft.com/fr-fr/dotnet/core/extensions/…
6 votes
Les transitoires signifient que si
MyClass
a besoin d'une instance deSomeDependency
, il en obtient une, et siSomeOtherClass
a besoin d'une instance, il obtient une nouvelle instance deSomeDependency
. Ce qui est différent de l'instance donnée àMyClass
. Scoped signifie (dans le contexte des requêtes HTTP en cours de traitement) qu'une instance deSomeDependency
, donnée àMyClass
etSomeOtherClass
, sera la même (pour la même requête HTTP en cours de traitement).2 votes
Si vous êtes un locuteur hindi. J'ai créé une très belle vidéo sur ce sujet qui explique le concept avec un exemple. youtu.be/OkOy8Q-OPGI
1 votes
Le mot demandé et une fois par demande est à l'origine de ces confusions. Bien que @MemetOlsen ait clarifié cela, j'ajoute une ligne supplémentaire. Le premier est demandé pour une nouvelle instance de l'objet de dépendance, que vous soyez sur le même appel HTTP ou non. Le second est la même instance de l'objet de dépendance à travers le même appel HTTP.
0 votes
Je l'oublie toujours. C'est tellement frustrant.