Unterschiede zwischen AddTransient-, AddScoped- und AddSingleton-Diensten


938

Ich möchte Dependency Injection (DI) in ASP.NET Core implementieren . Nach dem Hinzufügen dieses Codes zur ConfigureServicesMethode funktionieren beide Möglichkeiten.

Was ist der Unterschied zwischen den Methoden services.AddTransientund service.AddScopedin ASP.NET Core?

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

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

92
@tmg In den Dokumenten heißt es: "Transient Lifetime Services werden jedes Mal erstellt, wenn sie angefordert werden." und "Gültigkeitsdauer-Services werden einmal pro Anforderung erstellt." was, es sei denn, meine Englischkenntnisse sind schwächer als ich dachte, genau dasselbe bedeuten.
Neutrino

70
@tmg Ich weiß. Ich möchte nur darauf hinweisen, dass die Dokumente in diesem Punkt überhaupt nicht klar sind, daher ist es nicht sehr hilfreich, Leute auf die Dokumente zu verweisen.
Neutrino

13
@Neutrino, deshalb habe ich diese Frage gestellt.
Elvin Mammadov

5
Spät zur Party, um die Kommentare noch später zu lesen, aber ich habe diesen Artikel ausgedruckt, gelesen und die gleiche Beobachtung am Rand notiert, die ich jetzt hier bei @Neutrino sehe. Der Artikel war VOLLSTÄNDIG vage, als er diese Analyse anbot. Das Beispiel war zum Glück weniger verwirrend.
Wellspring

5
Soweit ich weiß: Transient Lifetime Services werden jedes Mal erstellt, wenn sie angefordert werden . Das hier angeforderte Wort ist die alltägliche englische Bedeutung, nach etwas zu fragen, in diesem Fall nach einer Dienstleistung. Während das Wort Anfrage in einmal pro Anfrage bezieht sich auf einen HTTP - Request. Aber ich verstehe die Verwirrung.
Memet Olsen

Antworten:


1652

TL; DR

Vorübergehende Objekte sind immer unterschiedlich. Jeder Steuerung und jedem Dienst wird eine neue Instanz bereitgestellt.

Objekte mit Gültigkeitsbereich sind innerhalb einer Anforderung gleich, unterscheiden sich jedoch in verschiedenen Anforderungen.

Singleton-Objekte sind für jedes Objekt und jede Anforderung gleich.

Zur Verdeutlichung zeigt dieses Beispiel aus der ASP.NET-Dokumentation den Unterschied:

Um den Unterschied zwischen diesen Lebensdauer- und Registrierungsoptionen zu demonstrieren, betrachten Sie eine einfache Schnittstelle, die eine oder mehrere Aufgaben als Vorgang mit einer eindeutigen Kennung darstellt OperationId. Abhängig davon, wie wir die Lebensdauer für diesen Dienst konfigurieren, stellt der Container der anfordernden Klasse entweder die gleichen oder verschiedene Instanzen des Dienstes zur Verfügung. Um zu verdeutlichen, welche Lebensdauer angefordert wird, erstellen wir einen Typ pro Lebenszeitoption:

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

Wir implementieren diese Schnittstellen mit einer einzelnen Klasse, Operationdie eine GUID in ihrem Konstruktor akzeptiert, oder verwenden eine neue GUID, wenn keine angegeben ist:

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

Als Nächstes wird ConfigureServicesjeder Typ entsprechend seiner benannten Lebensdauer zum Container hinzugefügt:

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

Beachten Sie, dass der IOperationSingletonInstanceDienst eine bestimmte Instanz mit einer bekannten ID von verwendet Guid.Empty, sodass klar ist, wann dieser Typ verwendet wird. Wir haben auch eine registriert OperationService, die von jedem der anderen OperationTypen abhängt , sodass innerhalb einer Anfrage klar wird, ob dieser Dienst für jeden Operationstyp dieselbe oder eine neue Instanz wie der Controller erhält. Dieser Dienst macht lediglich seine Abhängigkeiten als Eigenschaften verfügbar, damit sie in der Ansicht angezeigt werden können.

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

Um die Objektlebensdauer innerhalb und zwischen einzelnen Anforderungen an die Anwendung zu demonstrieren, enthält das Beispiel eine OperationsController, die jede Art von IOperationTyp anfordert, sowie eine OperationService. Die IndexAktion zeigt dann alle OperationIdWerte des Controllers und des Dienstes an .

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

Nun werden zwei separate Anforderungen an diese Controller-Aktion gestellt:

Erste Anfrage

Zweite Anfrage

Beobachten Sie, welcher der OperationIdWerte innerhalb einer Anforderung und zwischen Anforderungen variiert.

  • Vorübergehende Objekte sind immer unterschiedlich. Jeder Steuerung und jedem Dienst wird eine neue Instanz bereitgestellt.

  • Objekte mit Gültigkeitsbereich sind innerhalb einer Anforderung gleich, unterscheiden sich jedoch in verschiedenen Anforderungen

  • Singleton-Objekte sind für jedes Objekt und jede Anforderung gleich (unabhängig davon, ob eine Instanz in bereitgestellt wird ConfigureServices).


14
Ich habe die Funktionen der einzelnen verstanden, aber kann jemand die Auswirkungen der Verwendung der einen anstelle der anderen erklären? Welche Probleme kann es verursachen, wenn es nicht richtig verwendet wird oder wenn Sie eines anstelle eines anderen auswählen?
Pawan Nepal

2
Angenommen, Sie erstellen ein kontextbezogenes Anforderungsobjekt (wie der aktuelle Benutzer) mit Singleton-Gültigkeitsbereich, dann bleibt es über alle nicht gewünschten http-Anforderungen hinweg dieselbe Instanz. Bei IOC dreht sich alles um das Erstellen von Instanzen. Daher müssen wir den Umfang der erstellten Instanz angeben.
Akazemis

1
es ist!, ich habe den Link oben im Thema erwähnt! Der Beispielcode wird aus MS-Dokumenten kopiert / eingefügt
akazemis

1
Vielen Dank. Ja, Singleton ist in der gesamten App gleich, unabhängig von Sitzung / Benutzer. Wenn Ihre App die Microservices-Architektur verwendet und jeder Dienst in einem separaten Prozess ausgeführt wird, ist der Singleton in jedem Prozess derselbe
Akazemis

1
Können Sie uns bitte ein Beispiel für die Verwendung von addTransient geben? weil ich keine Dienstprogramme gefunden habe, um es zu verwenden, während es zu viele Ressourcen
verbraucht

318

Bei der Abhängigkeitsinjektion von .NET gibt es drei Hauptlebensdauern:

Singleton , das eine einzelne Instanz in der gesamten Anwendung erstellt. Es erstellt die Instanz zum ersten Mal und verwendet dasselbe Objekt in allen Aufrufen erneut.

Gültigkeitsdauer- Services werden einmal pro Anforderung innerhalb des Gültigkeitsbereichs erstellt. Dies entspricht einem Singleton im aktuellen Bereich. In MVC wird beispielsweise eine Instanz für jede HTTP-Anforderung erstellt, in den anderen Aufrufen innerhalb derselben Webanforderung wird jedoch dieselbe Instanz verwendet.

Transient Lifetime Services werden jedes Mal erstellt, wenn sie angefordert werden. Diese Lebensdauer eignet sich am besten für leichte, zustandslose Dienste.

Hier finden Sie Beispiele, um den Unterschied zu sehen:

ASP.NET 5 MVC6-Abhängigkeitsinjektion in 6 Schritten (Webarchivlink aufgrund eines toten Links)

Ihre Abhängigkeitsinjektion bereit ASP.NET: ASP.NET 5

Und das ist der Link zur offiziellen Dokumentation:

Abhängigkeitsinjektion in ASP.NET Core


22
Könnten Sie bitte erklären, warum der Transient am leichtesten ist? Ich dachte, der Transient ist die schwerste Arbeit, weil er jedes Mal für jede Injektion eine Instanz erstellen muss.
Experte will

17
Du hast recht. Transient ist nicht das leichteste, ich habe nur gesagt, es ist für leichte RESTful-Dienste geeignet :)
Akazemis

3
In welchem ​​Szenario könnten wir also einen Bereich verwenden und in welchem ​​Übergang im Controller-Beispiel, beispielsweise wenn wir nur wenige Zeilen aus der Datenbank abrufen? In diesem Fall versuche ich, das Szenario mit dem Umfang und der vorübergehenden Nutzung zu verstehen.
Sensei

4
es hängt wirklich von der Logik ab, die Sie erwarten. Wenn es sich beispielsweise um einen einzelnen Datenbankaufruf handelt, spielt es keine Rolle, welchen Sie verwenden. Wenn Sie db jedoch mehrmals in derselben Anforderung aufrufen, können Sie die Gültigkeitsdauer des Gültigkeitsbereichs verwenden, da dasselbe Repository-Objekt im Speicher verbleibt und mehrere Male im selben HTTP-Anforderungskontext wiederverwendet werden. Während das vorübergehende Objekt mehrmals ein neues Repository-Objekt erstellt (und mehr Speicher verbraucht). Wenn Sie Ihr spezifisches Szenario erklären, ist es leicht zu beurteilen, welches besser passt.
Akazemis

3
Ein wichtiger Punkt, der hier hervorgehoben werden muss, ist, dass Singleton, Scoped und Transient wie russische Doills ineinander sind. Es ist nicht möglich, ihre Reihenfolge beim Verschachteln umzukehren, z. Ein Gültigkeitsbereich oder ein Singleton kann nicht in einem Transienten enthalten sein, da wir die Lebensdauer des Elternteils verlängern würden, was gegen die Eindämmung verstößt!
DL Narasimhan

34

Transient, Scoped und Singleton definieren den Objekterstellungsprozess in ASP.NET MVC Core DI, wenn mehrere Objekte desselben Typs injiziert werden müssen. Falls Sie mit der Abhängigkeitsinjektion noch nicht vertraut sind, können Sie dieses DI IoC-Video sehen .

Sie können den folgenden Controller-Code sehen, in dem ich zwei Instanzen von "IDal" im Konstruktor angefordert habe. Transient, Scoped und Singleton definieren, ob dieselbe Instanz in "_dal" und "_dal1" oder anders eingefügt wird.

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 werden neue Objektinstanzen in eine einzelne Anforderung und Antwort eingefügt. Unten ist ein Schnappschuss, in dem ich GUID-Werte angezeigt habe.

Geben Sie hier die Bildbeschreibung ein

Gültigkeitsbereich: Im Gültigkeitsbereich wird dieselbe Objektinstanz in eine einzelne Anforderung und Antwort eingefügt.

Geben Sie hier die Bildbeschreibung ein

Singleton: In Singleton wird dasselbe Objekt über alle Anforderungen und Antworten hinweg injiziert. In diesem Fall wird eine globale Instanz des Objekts erstellt.

Unten finden Sie ein einfaches Diagramm, das die obigen Grundlagen visuell erklärt.

MVC DI-Bild

Das obige Bild wurde vom SBSS-Team gezeichnet, als ich an einem ASP.NET MVC-Training in Mumbai teilnahm . Ein großes Dankeschön geht an das SBSS-Team für die Erstellung des obigen Bildes.


9
Dies ist die komplizierteste Erklärung für einen vorübergehenden Dienst, den ich je gesehen habe. Transient = Jedes Mal, wenn dieser Dienst aufgelöst wird, entspricht dies der Zuweisung Ihrer Variablen new TService. Scoped speichert die erste Initialisierung für diesen "Bereich" zwischen (in den meisten Fällen http-Anforderung). Singleton speichert während der gesamten Lebensdauer der Anwendung nur eine Instanz, so einfach ist das. Die obigen Diagramme sind so kompliziert.
Mardoxx

2
Tut mir leid, ich dachte, ich werde es mit Diagrammen und Code-Schnappschuss einfacher machen :-) Aber ich verstehe, worum es geht.
Shivprasad Koirala

30
  • Singleton ist eine einzelne Instanz für die Lebensdauer der Anwendungsdomäne.
  • Gültigkeitsbereich ist eine einzelne Instanz für die Dauer der Gültigkeitsbereichsanforderung, dh pro HTTP Anforderung in ASP.NET.
  • Transient ist eine einzelne Instanz pro Codeanforderung .

Normalerweise sollte die Codeanforderung über einen Konstruktorparameter erfolgen, wie in

public MyConsumingClass(IDependency dependency)

Ich wollte in der Antwort von @ akazemis darauf hinweisen, dass "Dienste" im Kontext von DI keine RESTful-Dienste bedeuten. Services sind Implementierungen von Abhängigkeiten, die Funktionen bereitstellen.


16

AddSingleton ()

AddSingleton () erstellt eine einzelne Instanz des Dienstes, wenn dieser zum ersten Mal angefordert wird, und verwendet dieselbe Instanz an allen Stellen, an denen dieser Dienst benötigt wird.

AddScoped ()

In einem Scoped Service erhalten wir mit jeder HTTP-Anforderung eine neue Instanz. Wenn jedoch innerhalb derselben HTTP-Anforderung der Dienst an mehreren Stellen erforderlich ist, z. B. in der Ansicht und im Controller, wird dieselbe Instanz für den gesamten Umfang dieser HTTP-Anforderung bereitgestellt. Jede neue HTTP-Anforderung erhält jedoch eine neue Instanz des Dienstes.

AddTransient ()

Bei einem vorübergehenden Dienst wird jedes Mal eine neue Instanz bereitgestellt, wenn eine Dienstinstanz angefordert wird, unabhängig davon, ob sie sich im Bereich derselben HTTP-Anforderung oder über verschiedene HTTP-Anforderungen hinweg befindet.


5

Nachdem ich nach einer Antwort auf diese Frage gesucht hatte, fand ich eine brillante Erklärung mit einem Beispiel, das ich mit Ihnen teilen möchte.

Sie können sich HIER ein Video ansehen, das die Unterschiede zeigt

In diesem Beispiel haben wir diesen angegebenen 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();
    }
}

Ansicht erstellen

@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 diesen Code ein und klicken Sie auf die Schaltfläche Erstellen in der Ansicht und wechseln Sie zwischen AddSingleton, AddScopedund AddTransientSie werden jedes Mal ein anderes Ergebnis erhalten , dass Sie diese Erklärung verstehen könnte helfen:

AddSingleton () - Wie der Name schon sagt, erstellt die AddSingleton () -Methode einen Singleton-Dienst. Ein Singleton-Dienst wird erstellt, wenn er zum ersten Mal angefordert wird. Dieselbe Instanz wird dann von allen nachfolgenden Anforderungen verwendet. Im Allgemeinen wird ein Singleton-Dienst nur einmal pro Anwendung erstellt, und diese einzelne Instanz wird während der gesamten Anwendungslebensdauer verwendet.

AddTransient () - Diese Methode erstellt einen Transient-Dienst. Bei jeder Anforderung wird eine neue Instanz eines Transient-Dienstes erstellt.

AddScoped () - Diese Methode erstellt einen Scoped-Service. Eine neue Instanz eines Bereichsdienstes wird einmal pro Anforderung innerhalb des Bereichs erstellt. In einer Webanwendung wird beispielsweise 1 Instanz pro http-Anforderung erstellt, in den anderen Aufrufen innerhalb derselben Webanforderung wird jedoch dieselbe Instanz verwendet.


2

Welches zu verwenden

Vorübergehend

  • Da sie jedes Mal erstellt werden, verbrauchen sie mehr Speicher und Ressourcen und können sich negativ auf die Leistung auswirken
  • Verwenden Sie dies für den leichten Service mit wenig oder keinem Zustand .

Geltungsbereich

  • Bessere Option, wenn Sie den Status innerhalb einer Anforderung beibehalten möchten.

Singleton

  • Speicherlecks in diesen Diensten werden sich im Laufe der Zeit aufbauen.
  • auch speichereffizient, da sie einmal überall wiederverwendet werden.

Verwenden Sie Singletons, bei denen Sie den anwendungsweiten Status beibehalten müssen. Anwendungskonfiguration oder -parameter, Protokollierungsdienst, Zwischenspeichern von Daten sind einige Beispiele, bei denen Sie Singletons verwenden können.

Injizieren eines Dienstes mit unterschiedlichen Lebensdauern in einen anderen

  1. Fügen Sie niemals Scoped & Transient-Dienste in den Singleton-Dienst ein. (Dadurch wird der vorübergehende Dienst oder der Dienst mit Gültigkeitsbereich effektiv in den Singleton konvertiert.)
  2. Injizieren Sie niemals vorübergehende Dienste in den Bereichsdienst (Dies wandelt den vorübergehenden Dienst in den Bereich um.)

Dies ist die beste Antwort. Ich mag einen Teil, in dem Sie Beispiele geben. Es ist nicht so schwer zu verstehen, wie sie funktionieren. Es ist viel schwieriger zu überlegen, welcher Dienst wo abgelegt werden soll und wie und wann der Speicher von ihnen gereinigt wird. Es wäre großartig, wenn Sie mehr darüber erklären würden.
Valentasm

1

Wie hier beschrieben (dieser Link ist sehr nützlich) mit einem Beispiel,

Diese Zuordnung zwischen der Schnittstelle und dem konkreten Typ definiert, dass Sie jedes Mal, wenn Sie einen IContryService-Typ anfordern, eine neue Instanz des CountryService erhalten. Dies bedeutet in diesem Fall vorübergehend. Sie können auch Singleton-Zuordnungen (mit AddSingleton) und Bereichszuordnungen (mit AddScoped) hinzufügen. Gültigkeitsbereich bedeutet in diesem Fall, auf eine HTTP-Anforderung beschränkt zu sein. Dies bedeutet auch, dass es sich um einen Singleton handelt, während die aktuelle Anforderung ausgeführt wird. Sie können dem DI-Container auch eine vorhandene Instanz mit der Methode AddInstance hinzufügen. Dies sind die fast vollständigen Möglichkeiten, sich bei der IServiceCollection zu registrieren


1

Der Unterschied zwischen AddSingleton und AddScoped und AddTransient

Registrieren von Diensten

Der ASP.NET-Kern bietet die folgenden drei Methoden zum Registrieren von Diensten im Abhängigkeitsinjektionscontainer. Die von uns verwendete Methode bestimmt die Lebensdauer des registrierten Dienstes.

AddSingleton () - Wie der Name schon sagt, erstellt die AddSingleton () -Methode einen Singleton-Dienst. Ein Singleton-Dienst wird erstellt, wenn er zum ersten Mal angefordert wird. Dieselbe Instanz wird dann von allen nachfolgenden Anforderungen verwendet. Im Allgemeinen wird ein Singleton-Dienst nur einmal pro Anwendung erstellt, und diese einzelne Instanz wird während der gesamten Anwendungslebensdauer verwendet.

AddTransient () - Diese Methode erstellt einen Transient-Dienst. Bei jeder Anforderung wird eine neue Instanz eines Transient-Dienstes erstellt.

AddScoped () - Diese Methode erstellt einen Scoped-Service. Eine neue Instanz eines Bereichsdienstes wird einmal pro Anforderung innerhalb des Bereichs erstellt. In einer Webanwendung wird beispielsweise 1 Instanz pro http-Anforderung erstellt, in den anderen Aufrufen innerhalb derselben Webanforderung wird jedoch dieselbe Instanz verwendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.