Dependency Injection in ASP.NET CoreDependency injection in ASP.NET Core

Von Steve Smith, Scott Addie, und Luke LathamBy Steve Smith, Scott Addie, and Luke Latham

ASP.NET Core unterstützt das Softwareentwurfsmuster Abhängigkeitsinjektion. Damit kann eine Umkehrung der Steuerung (Inversion of Control, IoC) zwischen Klassen und ihren Abhängigkeiten erreicht werden.ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

Weitere Informationen zur Abhängigkeitsinjektion innerhalb von MVC-Controllern finden Sie unter Dependency Injection in Controller in ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Dependency Injection in Controller in ASP.NET Core.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

Übersicht über AbhängigkeitsinjektionOverview of dependency injection

Eine Abhängigkeit ist ein beliebiges Objekt, das ein anderes Objekt benötigt.A dependency is any object that another object requires. Überprüfen Sie die folgende MyDependency-Klasse mit einer WriteMessage-Methode, von der andere Klassen in einer App abhängen:Examine the following MyDependency class with a WriteMessage method that other classes in an app depend upon:

public class MyDependency
{
    public MyDependency()
    {
    }

    public Task WriteMessage(string message)
    {
        Console.WriteLine(
            $"MyDependency.WriteMessage called. Message: {message}");

        return Task.FromResult(0);
    }
}

Eine Instanz der Klasse MyDependency kann erstellt werden, um die WriteMessage-Methode einer Klasse zur Verfügung zu stellen.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. Die Klasse MyDependency ist eine Abhängigkeit der Klasse IndexModel:The MyDependency class is a dependency of the IndexModel class:

public class IndexModel : PageModel
{
    MyDependency _dependency = new MyDependency();

    public async Task OnGetAsync()
    {
        await _dependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Die Klasse erstellt die Instanz MyDependency und hängt direkt von dieser ab.The class creates and directly depends on the MyDependency instance. Codeabhängigkeiten (wie im vorherigen Beispiel) sind problematisch und sollten aus folgenden Gründen vermieden werden:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Um MyDependency durch eine andere Implementierung zu ersetzen, muss die Klasse geändert werden.To replace MyDependency with a different implementation, the class must be modified.
  • Wenn MyDependency über Abhängigkeiten verfügt, müssen diese von der Klasse konfiguriert werden.If MyDependency has dependencies, they must be configured by the class. In einem großen Projekt mit mehreren Klassen, die von MyDependency abhängig sind, wird der Konfigurationscode über die App verteilt.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Diese Implementierung ist nicht für Komponententests geeignet.This implementation is difficult to unit test. Die App sollte eine MyDependency-Modell- oder Stubklasse verwenden, was mit diesem Ansatz nicht möglich ist.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

Die Abhängigkeitsinjektion löst dieses Problem mithilfe der folgenden Schritte:Dependency injection addresses these problems through:

  • Die Verwendung einer Schnittstelle oder Basisklasse zur Abstraktion der Abhängigkeitsimplementierung.The use of an interface or base class to abstract the dependency implementation.
  • Registrierung der Abhängigkeit in einem Dienstcontainer.Registration of the dependency in a service container. ASP.NET Core stellt einen integrierten Dienstcontainer (IServiceProvider) bereit.ASP.NET Core provides a built-in service container, IServiceProvider. Die Dienste werden in der App-Methode Startup.ConfigureServices registriert.Services are registered in the app's Startup.ConfigureServices method.
  • Die Injektion des Diensts in den Konstruktor der Klasse, wo er verwendet wird.Injection of the service into the constructor of the class where it's used. Das Framework erstellt eine Instanz der Abhängigkeit und entfernt diese, wenn sie nicht mehr benötigt wird.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

In der Beispiel-App definiert die IMyDependency-Schnittstelle eine Methode, die der Dienst für die App bereitstellt:In the sample app, the IMyDependency interface defines a method that the service provides to the app:

public interface IMyDependency
{
    Task WriteMessage(string message);
}

Diese Schnittstelle wird durch einen konkreten Typ (MyDependency) implementiert:This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}", 
            message);

        return Task.FromResult(0);
    }
}

MyDependency fordert einen ILogger<TCategoryName> beim zugehörigen Konstruktor an.MyDependency requests an ILogger<TCategoryName> in its constructor. Die Abhängigkeitsinjektion wird häufig als Verkettung verwendet.It's not unusual to use dependency injection in a chained fashion. Jede angeforderte Abhängigkeit fordert wiederum ihre eigenen Abhängigkeiten an.Each requested dependency in turn requests its own dependencies. Der Container löst die Abhängigkeiten im Diagramm auf und gibt den vollständig aufgelösten Dienst zurück.The container resolves the dependencies in the graph and returns the fully resolved service. Die gesammelten aufzulösenden Abhängigkeiten werden als Abhängigkeitsstruktur, Abhängigkeitsdiagramm oder Objektdiagramm bezeichnet.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency und ILogger<TCategoryName> müssen im Dienstcontainer registriert werden.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency ist in Startup.ConfigureServices registriert.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> wird von der Protokollierungsabstraktionsinfrastruktur registriert. Es handelt sich also um einen von einem Framework bereitgestellten Dienst, der standardmäßig vom Framework registriert wird.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

Der Container löst ILogger<TCategoryName> unter Verwendung der (generischen) offenen Typen auf, wodurch die Notwendigkeit entfällt, jeden (generischen) konstruierten Typ zu registrieren:The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type:

services.AddSingleton(typeof(ILogger<T>), typeof(Logger<T>));

In der Beispiel-App ist der Dienst IMyDependency mit dem konkreten Typ MyDependency registriert.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. Die Registrierung schränkt die Lebensdauer des Diensts auf die Lebensdauer einer einzelnen Anforderung ein.The registration scopes the service lifetime to the lifetime of a single request. Auf die Dienstlebensdauer wird später in diesem Artikel eingegangen.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

Hinweis

Jede Erweiterungsmethode vom Typ services.Add{SERVICE_NAME} fügt Dienste hinzu (und konfiguriert diese möglicherweise).Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. So fügt beispielsweise services.AddMvc() die erforderlichen Dienste Razor Pages und MVC hinzu.For example, services.AddMvc() adds the services Razor Pages and MVC require. Es wird empfohlen, dass Apps dieser Konvention folgen.We recommended that apps follow this convention. Platzieren Sie Erweiterungsmethoden im Namespace Microsoft.Extensions.DependencyInjection, um Gruppen von Dienstregistrierungen zu kapseln.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Wenn für den Dienstkonstruktor ein integrierter Typ erforderlich ist, z. B. string, kann dieser Typ über Konfiguration oder das Optionsmuster eingefügt werden:If the service's constructor requires a built in type, such as a string, the type can be injected by using configuration or the options pattern:

public class MyDependency : IMyDependency
{
    public MyDependency(IConfiguration config)
    {
        var myStringValue = config["MyStringKey"];

        // Use myStringValue
    }

    ...
}

Eine Instanz des Diensts wird über den Konstruktor einer Klasse angefordert, in der der Dienst verwendet wird, und einem privaten Feld zugewiesen.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. Das Feld wird verwendet, um auf den Dienst bei Bedarf über die gesamte Klasse zuzugreifen.The field is used to access the service as necessary throughout the class.

In der Beispiel-App wird die Instanz IMyDependency angefordert, und mit ihr wird die App-Methode WriteMessage aufgerufen:In the sample app, the IMyDependency instance is requested and used to call the service's WriteMessage method:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Von Frameworks bereitgestellte DiensteFramework-provided services

Die Methode Startup.ConfigureServices definiert die von der App verwendeten Dienste. Dies beinhaltet Plattformfunktionen wie Entity Framework Core und ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Zunächst enthält die in ConfigureServices bereitgestellte IServiceCollection folgende definierten Dienste (abhängig von der Vorgehensweise bei der Konfiguration des Hosts):Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on how the host was configured):

DiensttypService Type LebensdauerLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Transient (vorübergehend)Transient
Microsoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter Transient (vorübergehend)Transient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory Transient (vorübergehend)Transient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Transient (vorübergehend)Transient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Wenn eine Dienstsammlungs-Erweiterungsmethode verfügbar ist, um einen Dienst (und ggf. seine abhängigen Dienste) zu registrieren, ist die Konvention, eine einzige Add{SERVICE_NAME}-Erweiterungsmethode zu verwenden, um alle von diesem Dienst benötigten Dienste zu registrieren.When a service collection extension method is available to register a service (and its dependent services, if required), the convention is to use a single Add{SERVICE_NAME} extension method to register all of the services required by that service. Der folgende Code ist ein Beispiel für das Hinzufügen zusätzlicher Dienste zum Container mit den Erweiterungsmethoden AddDbContext<TContext>, AddIdentityCore und AddMvc:The following code is an example of how to add additional services to the container using the extension methods AddDbContext<TContext>, AddIdentityCore, and AddMvc:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();
}

Weitere Informationen finden Sie in der ServiceCollection-Klasse in der API-Dokumentation.For more information, see the ServiceCollection class in the API documentation.

DienstlebensdauerService lifetimes

Wählen Sie eine geeignete Lebensdauer für jeden registrierten Dienst aus.Choose an appropriate lifetime for each registered service. ASP.NET Core-Dienste können mit folgender Lebensdauer konfiguriert werden:ASP.NET Core services can be configured with the following lifetimes:

Transient (vorübergehend)Transient

Kurzlebige Dienste (AddTransient) werden bei jeder Anforderung aus dem Dienstcontainer neu erstellt.Transient lifetime services (AddTransient) are created each time they're requested from the service container. Diese Lebensdauer ist am besten für einfache, zustandslose Dienste geeignet.This lifetime works best for lightweight, stateless services.

BereichsbezogenScoped

Dienste mit bereichsbezogener Lebensdauer (AddScoped) werden einmal pro Clientanforderung (-verbindung) erstellt.Scoped lifetime services (AddScoped) are created once per client request (connection).

Warnung

Wenn Sie einen bereichsbezogenen Dienst in einer Middleware verwenden, müssen Sie den Dienst in die Invoke- oder InvokeAsync-Methode einfügen.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Fügen Sie ihn nicht über Constructor Injection ein, da hierdurch der Dienst ein Verhalten wie ein Singleton zeigt.Don't inject via constructor injection because it forces the service to behave like a singleton. Weitere Informationen finden Sie unter Schreiben von benutzerdefinierter ASP.NET Core-Middleware.For more information, see Schreiben von benutzerdefinierter ASP.NET Core-Middleware.

SingletonSingleton

Dienste mit Singleton-Lebensdauer (AddSingleton) werden bei der ersten Anforderung erstellt (bzw. wenn Startup.ConfigureServices ausgeführt wird, und eine Instanz bei der Dienstregistrierung angegeben wird).Singleton lifetime services (AddSingleton) are created the first time they're requested (or when Startup.ConfigureServices is run and an instance is specified with the service registration). Jeder nachfolgenden Anforderung verwendet die gleiche Instanz.Every subsequent request uses the same instance. Wenn die App ein Verhalten vom Typ „Singleton“ erfordert, wird empfohlen, den Dienstcontainer die Dienstlebensdauer verwalten zu lassen.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Implementieren Sie nicht das Designmuster „Singleton“ und stellen Sie Benutzercode bereit, um die Objektlebensdauer in der Klasse zu verwalten.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

Warnung

Es ist riskant, einen bereichsbezogenen Dienst über ein Singleton aufzulösen.It's dangerous to resolve a scoped service from a singleton. Möglicherweise weist der Dienst bei der Verarbeitung nachfolgender Anforderungen einen falschen Status auf.It may cause the service to have incorrect state when processing subsequent requests.

DienstregistrierungsmethodenService registration methods

Jede Methode zur Erweiterung der Dienstregistrierung bietet Überladungen, die in bestimmten Szenarios hilfreich sind.Each service registration extension method offers overloads that are useful in specific scenarios.

MethodeMethod AutomatischeAutomatic
Objektobject
bereinigungdisposal
MehrereMultiple
Implementierungenimplementations
ArgumentübergabePass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Beispiel:Example:
services.AddScoped<IMyDep, MyDep>();
JaYes JaYes NeinNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Beispiele:Examples:
services.AddScoped<IMyDep>(sp => new MyDep());
services.AddScoped<IMyDep>(sp => new MyDep("A string!"));
JaYes JaYes JaYes
Add{LIFETIME}<{IMPLEMENTATION}>()
Beispiel:Example:
services.AddScoped<MyDep>();
JaYes NeinNo NeinNo
Add{LIFETIME}<{SERVICE}>(new {IMPLEMENTATION})
Beispiele:Examples:
services.AddScoped<IMyDep>(new MyDep());
services.AddScoped<IMyDep>(new MyDep("A string!"));
NeinNo JaYes JaYes
Add{LIFETIME}(new {IMPLEMENTATION})
Beispiele:Examples:
services.AddScoped(new MyDep());
services.AddScoped(new MyDep("A string!"));
NeinNo NeinNo JaYes

Weitere Informationen zum Löschen von Typen finden Sie im Abschnitt Löschen von Diensten.For more information on type disposal, see the Disposal of services section. Ein häufiges Szenario für mehrere Implementierungen ist Typen zu Testzwecken simulieren.A common scenario for multiple implementations is mocking types for testing.

TryAdd{LIFETIME}-Methoden registrieren den Dienst nur, wenn noch keine Implementierung registriert ist.TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

Im folgenden Beispiel registriert die erste Zeile MyDependency für IMyDependency.In the following example, the first line registers MyDependency for IMyDependency. Die zweite Zeile hat keine Auswirkungen, da es für IMyDependency bereits eine registrierte Implementierung gibt:The second line has no effect because IMyDependency already has a registered implementation:

services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

Weitere Informationen finden Sie unter:For more information, see:

TryAddEnumerable(ServiceDescriptor)-Methoden registrieren den Dienst nur, wenn es nicht bereits eine Implementierung des gleichen Typs gibt.TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. Mehrere Dienste werden über IEnumerable<{SERVICE}> aufgelöst.Multiple services are resolved via IEnumerable<{SERVICE}>. Beim Registrieren von Diensten möchte der Entwickler nur eine Instanz hinzufügen, wenn nicht bereits eine Instanz vom gleichen Typ hinzugefügt wurde.When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. Diese Methode wird in der Regel von Bibliotheksautoren verwendet, um zu vermeiden, dass zwei Kopien einer Instanz im Container registriert werden.Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

Im folgenden Beispiel registriert die erste Zeile MyDep für IMyDep1.In the following example, the first line registers MyDep for IMyDep1. Die zweite Zeile registriert MyDep für IMyDep2.The second line registers MyDep for IMyDep2. Die dritte Zeile hat keine Auswirkungen, da es für IMyDep1 bereits eine registrierte Implementierung von MyDep gibt:The third line has no effect because IMyDep1 already has a registered implementation of MyDep:

public interface IMyDep1 {}
public interface IMyDep2 {}

public class MyDep : IMyDep1, IMyDep2 {}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep2, MyDep>());
// Two registrations of MyDep for IMyDep1 is avoided by the following line:
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());

Verhalten von Constructor InjectionConstructor injection behavior

Dienste können durch zwei Mechanismen aufgelöst werden:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities – lässt die Erstellung von Objekten ohne Dienstregistrierung im Abhängigkeitsinjektionscontainer zu.ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilities wird mit Abstraktionen für Benutzer verwendet. Dazu zählen Taghilfsprogramme, MVC-Controller und Modellbindungen.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Konstruktoren können Argumente akzeptieren, die nicht durch Abhängigkeitsinjektion bereitgestellt werden. Die Argumente müssen jedoch Standardwerte zuweisen.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Wenn Dienste durch IServiceProvider oder ActivatorUtilities aufgelöst werden, benötigt Constructor Injection einen öffentlichen Konstruktor.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Wenn Dienste durch ActivatorUtilities aufgelöst werden, erfordert Constructor Injection, dass nur ein anwendbarer Konstruktor vorhanden ist.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Konstruktorüberladungen werden unterstützt. Es darf jedoch nur eine Überladung vorhanden sein, deren Argumente alle durch Dependency Injection erfüllt werden können.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Entity Framework-KontexteEntity Framework contexts

Entity Framework-Kontexte werden einem Dienstcontainer normalerweise mithilfe der bereichsbezogenen Lebensdauer hinzugefügt, da Datenbankvorgänge von Web-Apps normalerweise auf den Clientanforderungsbereich bezogen werden.Entity Framework contexts are usually added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. Der Bereich einer Standardlebensdauer wird festgelegt, wenn eine Lebensdauer nicht von einer AddDbContext<TContext>-Überladung angegeben wird, wenn der Datenbankkontext registriert wird.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. Dienste einer festgelegten Lebensdauer sollten keinen Datenbankkontext mit einer kürzeren Lebensdauer als die des Dienstes verwenden.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Lebensdauer und RegistrierungsoptionenLifetime and registration options

Um den Unterschied zwischen der Lebensdauer und den Registrierungsoptionen zu demonstrieren, betrachten Sie die folgenden Schnittstellen, die Aufgaben als Vorgänge mit einem eindeutigen Bezeichner (OperationId) darstellen.To demonstrate the difference between the lifetime and registration options, consider the following interfaces that represent tasks as an operation with a unique identifier, OperationId. Je nachdem, wie die Dienstlebensdauer für die folgenden Schnittstellen konfiguriert ist, stellt der Container auf Anforderung einer Klasse entweder die gleiche oder eine andere Instanz des Diensts zur Verfügung:Depending on how the lifetime of an operations service is configured for the following interfaces, the container provides either the same or a different instance of the service when requested by a class:

public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}

Die Schnittstellen sind in die Klasse Operation implementiert.The interfaces are implemented in the Operation class. Der Operation-Konstruktor generiert eine GUID, wenn noch keine angegeben ist:The Operation constructor generates a GUID if one isn't supplied:

public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}

OperationService ist registriert und hängt von jedem anderen Operation-Typ ab.An OperationService is registered that depends on each of the other Operation types. Wenn OperationService über die Abhängigkeitsinjektion angefordert wird, wird entweder eine neue Instanz jedes Diensts oder eine vorhandene Instanz basierend auf der Lebensdauer des abhängigen Diensts zurückgegeben.When OperationService is requested via dependency injection, it receives either a new instance of each service or an existing instance based on the lifetime of the dependent service.

  • Wenn vorübergehende Dienste bei der Anforderung aus dem Container erstellt werden, ist die OperationId von Dienst IOperationTransient anders als die OperationId von OperationService.When transient services are created when requested from the container, the OperationId of the IOperationTransient service is different than the OperationId of the OperationService. OperationService erhält eine neue Instanz der Klasse IOperationTransient.OperationService receives a new instance of the IOperationTransient class. Der OperationId-Wert der neuen Instanz ist anders.The new instance yields a different OperationId.
  • Wenn bereichsbezogene Dienste pro Clientanforderung erstellt werden, ist die OperationId in Dienst IOperationScoped und OperationService identisch innerhalb einer Clientanforderung.When scoped services are created per client request, the OperationId of the IOperationScoped service is the same as that of OperationService within a client request. Clientanforderungsübergreifend haben die beiden Dienste jedoch einen anderen gemeinsamen OperationId-Wert.Across client requests, both services share a different OperationId value.
  • Wenn Singleton- und Singletoninstanzdienste einmal erstellt und für alle Clientanforderungen und alle Dienste verwendet werden, ist OperationId für alle Dienstanforderungen identisch.When singleton and singleton-instance services are created once and used across all client requests and all services, the OperationId is constant across all service requests.
public class OperationService
{
    public OperationService(
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}

In Startup.ConfigureServices wird jeder Typ entsprechend seiner benannten Lebensdauer dem Container hinzugefügt:In Startup.ConfigureServices, each type is added to the container according to its named lifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

Der Dienst IOperationSingletonInstance verwendet eine bestimmte Instanz mit einer bekannten ID von Guid.Empty.The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. Die Verwendung dieses Typs ist eindeutig (die GUID besteht ausschließlich aus Nullen (0)).It's clear when this type is in use (its GUID is all zeroes).

Die Beispiel-App zeigt die Objektlebensdauer innerhalb einer Anforderung und zwischen einzelnen Anforderungen an.The sample app demonstrates object lifetimes within and between individual requests. Ihre Klasse IndexModel fordert jeden IOperation- und OperationService-Typ an.The sample app's IndexModel requests each kind of IOperation type and the OperationService. Die Seite zeigt dann alle OperationId-Werte der Seitenmodellklasse und des Diensts über Eigenschaftenzuweisungen an:The page then displays all of the page model class's and service's OperationId values through property assignments:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Zwei folgende Ausgaben zeigen die Ergebnisse von zwei Anforderungen:Two following output shows the results of two requests:

Erste Anforderung:First request:

Controllervorgänge:Controller operations:

Vorübergehend: d233e165-f417-469b-a866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Bereichsbezogen: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instanz: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService-Vorgänge:OperationService operations:

Vorübergehend: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Bereichsbezogen: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instanz: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Zweite Anforderung:Second request:

Controllervorgänge:Controller operations:

Vorübergehend: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Bereichsbezogen: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instanz: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService-Vorgänge:OperationService operations:

Vorübergehend: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Bereichsbezogen: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instanz: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Beachten Sie, welche der OperationId-Werte innerhalb einer Anforderung und zwischen Anforderungen variieren:Observe which of the OperationId values vary within a request and between requests:

  • Objekte vom Typ Vorübergehend sind immer unterschiedlich.Transient objects are always different. Der vorübergehende OperationId-Wert für die ersten und zweiten Clientanforderungen ist für beide OperationService-Vorgänge und clientanforderungsübergreifend unterschiedlich.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Eine neue Instanz wird für jede Dienstanforderung und jede Clientanforderung bereitgestellt.A new instance is provided to each service request and client request.
  • Bereichsbezogene Objekte sind innerhalb einer Clientanforderung identisch, clientanforderungsübergreifend sind sie jedoch unterschiedlich.Scoped objects are the same within a client request but different across client requests.
  • Objekte vom Typ Singleton sind bei jedem Objekt und in jeder Anforderung identisch (unabhängig davon, ob eine Operation-Instanz in Startup.ConfigureServices bereitgestellt wird).Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

Abrufen von Diensten aus „Main“Call services from main

Erstellen Sie IServiceScope mit IServiceScopeFactory.CreateScope, um einen bereichsbezogenen Dienst innerhalb des Anwendungsbereichs aufzulösen.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Dieser Ansatz eignet sich gut dafür, beim Start auf einen bereichsbezogenen Dienst zuzugreifen und Initialisierungsaufgaben auszuführen.This approach is useful to access a scoped service at startup to run initialization tasks. Das folgende Beispiel veranschaulicht, wie man einen Kontext für MyScopedService in Program.Main erhält:The following example shows how to obtain a context for the MyScopedService in Program.Main:

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;

        try
        {
            var serviceContext = services.GetRequiredService<MyScopedService>();
            // Use the context here
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred.");
        }
    }

    host.Run();
}

BereichsvalidierungScope validation

Wenn die App in der Entwicklungsumgebung ausgeführt wird, überprüft der Standarddienstanbieter, ob:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Bereichsbezogene Dienste nicht direkt oder indirekt vom Stammdienstanbieter aufgelöst werdenScoped services aren't directly or indirectly resolved from the root service provider.
  • Bereichsbezogene Dienste nicht direkt oder indirekt in Singletons eingefügt werdenScoped services aren't directly or indirectly injected into singletons.

Der Stammdienstanbieter wird erstellt, wenn BuildServiceProvider aufgerufen wird.The root service provider is created when BuildServiceProvider is called. Die Lebensdauer des Stammdienstanbieters entspricht der Lebensdauer der App/des Servers, wenn der Anbieter mit der App erstellt wird und verworfen wird, wenn die App beendet wird.The root service provider's lifetime corresponds to the app/server's lifetime when the provider starts with the app and is disposed when the app shuts down.

Bereichsbezogene Dienste werden von dem Container verworfen, der sie erstellt hat.Scoped services are disposed by the container that created them. Wenn ein bereichsbezogener Dienst im Stammcontainer erstellt wird, wird die Lebensdauer effektiv auf Singleton heraufgestuft, da er nur vom Stammcontainer verworfen wird, wenn die App/der Server heruntergefahren wird.If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when app/server is shut down. Die Überprüfung bereichsbezogener Dienste erfasst diese Situationen, wenn BuildServiceProvider aufgerufen wird.Validating service scopes catches these situations when BuildServiceProvider is called.

Weitere Informationen finden Sie unter ASP.NET Core-Webhost.For more information, see ASP.NET Core-Webhost.

Anfordern von DienstenRequest Services

Die Dienste, die innerhalb einer ASP.NET Core-Anforderung von HttpContext verfügbar sind, werden über die HttpContext.RequestService-Sammlung verfügbar gemacht.The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Anforderungsdienste stellen die Dienste dar, die als Teil der App konfiguriert und angefordert werden.Request Services represent the services configured and requested as part of the app. Wenn Objekte Abhängigkeiten angeben, werden diese von den in RequestServices gefundenen Typen erfüllt (nicht in ApplicationServices).When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Generell sollte die App diese Eigenschaften nicht direkt verwenden.Generally, the app shouldn't use these properties directly. Fordern Sie stattdessen die von Klassen benötigten Typen über Klassenkonstruktoren an, und lassen Sie das Framework die Abhängigkeiten einfügen.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. So erhalten Sie Klassen, die einfacher getestet werden können.This yields classes that are easier to test.

Hinweis

Fordern Sie für den Zugriff auf die RequestServices-Sammlung Abhängigkeiten lieber als Konstruktorparameter an.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Entwerfen von Diensten für die AbhängigkeitsinjektionDesign services for dependency injection

Best Practices:Best practices are to:

  • Entwerfen Sie Dienste zur Verwendung der Abhängigkeitsinjektion, um ihre Abhängigkeiten zu erhalten.Design services to use dependency injection to obtain their dependencies.
  • Vermeiden Sie zustandsbehaftete, statische Methodenaufrufe.Avoid stateful, static method calls.
  • Vermeiden Sie die direkte Instanziierung abhängiger Klassen innerhalb von Diensten.Avoid direct instantiation of dependent classes within services. Die direkte Instanziierung koppelt den Code an eine bestimmte Implementierung.Direct instantiation couples the code to a particular implementation.
  • Erstellen Sie kleine, gut gestaltete und einfach zu testende Apps.Make app classes small, well-factored, and easily tested.

Wenn eine Klasse zu viele eingefügte Abhängigkeiten zu haben scheint, ist dies im Allgemeinen ein Zeichen dafür, dass die Klasse zu viele Aufgaben hat und gegen das Single-Responsibility-Prinzip (SRP) (Prinzip der eindeutigen Verantwortlichkeit) verstößt.If a class seems to have too many injected dependencies, this is generally a sign that the class has too many responsibilities and is violating the Single Responsibility Principle (SRP). Versuchen Sie, die Klasse umzugestalten, indem Sie einige ihrer Aufgaben in eine neue Klasse verschieben.Attempt to refactor the class by moving some of its responsibilities into a new class. Beachten Sie, dass der Fokus der Razor Pages-Seitenmodellklassen und MVC-Controllerklassen auf der Benutzeroberfläche liegt.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Geschäftsregeln und Implementierungsdetails für den Datenzugriff sollten in Klassen aufbewahrt werden gemäß dem Prinzip Separation of Concerns (Trennung der Zuständigkeiten).Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Löschen von DienstenDisposal of services

Der Container ruft Dispose für die erstellten IDisposable-Typen auf.The container calls Dispose for the IDisposable types it creates. Wenn eine Instanz dem Container per Benutzercode hinzugefügt wird, wird sie nicht automatisch gelöscht.If an instance is added to the container by user code, it isn't disposed automatically.

// Services that implement IDisposable:
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}
public class Service3 : IDisposable {}

public interface ISomeService {}
public class SomeServiceImplementation : ISomeService, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    // The container creates the following instances and disposes them automatically:
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());

    // The container doesn't create the following instances, so it doesn't dispose of
    // the instances automatically:
    services.AddSingleton<Service3>(new Service3());
    services.AddSingleton(new Service3());
}

Ersetzen von StandarddienstcontainernDefault service container replacement

Der integrierte Dienstcontainer dient dazu, die Anforderungen des Frameworks und der meisten Consumer-Apps zu erfüllen.The built-in service container is meant to serve the needs of the framework and most consumer apps. Die Verwendung der integrierten Container wird empfohlen, es sei denn, Sie benötigen ein bestimmtes Feature, das nicht unterstützt wird.We recommend using the built-in container unless you need a specific feature that it doesn't support. Im Folgenden werden einige der Features aufgeführt, die von Containern von Drittanbietern unterstützt werden, aber nicht im integrierten Container enthalten sind:Some of the features supported in 3rd party containers not found in the built-in container:

  • EigenschaftsinjektionProperty injection
  • Auf Namen basierende InjektionInjection based on name
  • Untergeordnete ContainerChild containers
  • Benutzerdefinierte Verwaltung der LebensdauerCustom lifetime management
  • Func<T>-Unterstützung für die verzögerte InitialisierungFunc<T> support for lazy initialization

Eine Liste einiger Container, die Adapter unterstützen, finden Sie in der Datei „readme.md“ zur Dependency Injection.See the Dependency Injection readme.md file for a list of some of the containers that support adapters.

Im folgenden Beispiel wird der integrierte Container durch Autofac ersetzt:The following sample replaces the built-in container with Autofac:

  • Installieren Sie das entsprechende Containerpaket bzw. die entsprechenden Containerpakete:Install the appropriate container package(s):

  • Konfigurieren Sie den Container in Startup.ConfigureServices, und geben Sie einen IServiceProvider-Wert zurück:Configure the container in Startup.ConfigureServices and return an IServiceProvider:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        // Add other framework services
    
        // Add Autofac
        var containerBuilder = new ContainerBuilder();
        containerBuilder.RegisterModule<DefaultModule>();
        containerBuilder.Populate(services);
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    

    Um einen Drittanbietercontainer zu verwenden, muss Startup.ConfigureServices IServiceProvider zurückgeben.To use a 3rd party container, Startup.ConfigureServices must return IServiceProvider.

  • Konfigurieren Sie Autofac in DefaultModule:Configure Autofac in DefaultModule:

    public class DefaultModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
        }
    }
    

Zur Laufzeit wird Autofac verwendet, um Typen aufzulösen und Abhängigkeiten einzufügen.At runtime, Autofac is used to resolve types and inject dependencies. Weitere Informationen zur Verwendung von Autofac mit ASP.NET Core finden Sie in der Autofac-Dokumentation.To learn more about using Autofac with ASP.NET Core, see the Autofac documentation.

ThreadsicherheitThread safety

Erstellen Sie threadsichere Singleton-Dienste.Create thread-safe singleton services. Wenn ein Singleton-Dienst eine Abhängigkeit von einem vorübergehenden Dienst aufweist, muss der vorübergehende Dienst abhängig von der Verwendungsweise durch das Singleton ebenfalls threadsicher sein.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending how it's used by the singleton.

Die Factorymethode des einzelnen Diensts, z. B. das zweite Argument für AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), muss nicht threadsicher sein.The factory method of single service, such as the second argument to AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), doesn't need to be thread-safe. Wie bei static-Konstruktoren erfolgt der Aufruf einmalig über einen einzelnen Thread.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

EmpfehlungenRecommendations

  • async/await- und Task-basierte Dienstauflösung wird nicht unterstützt.async/await and Task based service resolution is not supported. C# unterstützt keine asynchronen Konstruktoren. Daher wird empfohlen, asynchrone Methoden zu verwenden, nachdem der Dienst synchron aufgelöst wurde.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Vermeiden Sie das Speichern von Daten und die direkte Konfiguration im Dienstcontainer.Avoid storing data and configuration directly in the service container. Der Einkaufswagen eines Benutzers sollte z. B. normalerweise nicht dem Dienstcontainer hinzugefügt werden.For example, a user's shopping cart shouldn't typically be added to the service container. Bei der Konfiguration sollte das Optionsmuster verwendet werden.Configuration should use the options pattern. Gleichermaßen sollten Sie „Datencontainer“-Objekte vermeiden, die nur vorhanden sind, um den Zugriff auf einige andere Objekte zuzulassen.Similarly, avoid "data holder" objects that only exist to allow access to some other object. Das tatsächlich benötige Element sollte besser über Dependency Injection angefordert werden.It's better to request the actual item via DI.

  • Vermeiden Sie den statischen Zugriff auf Dienste (z. B. statische Eingabe von IApplicationBuilder.ApplicationServices zur Verwendung an anderer Stelle).Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • Vermeiden Sie die Verwendung von Dienstlocator-Mustern.Avoid using the service locator pattern. Rufen Sie beispielsweise nicht GetService auf, um eine Dienstinstanz zu erhalten, wenn Sie stattdessen Dependency Injection verwenden können:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    Falsch:Incorrect:

    public class MyClass()
    {
        public void MyMethod()
        {
            var optionsMonitor = 
                _services.GetService<IOptionsMonitor<MyOptions>>();
            var option = optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    

    Richtig:Correct:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Eine andere Dienstlocator-Variante, die Sie vermeiden sollten, ist die Injektion einer Factory, die zur Laufzeit Abhängigkeiten auflöst.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Beide Vorgehensweisen kombinieren Strategien zur Umkehrung der Steuerung.Both of these practices mix Inversion of Control strategies.

  • Vermeiden Sie den statischen Zugriff auf HttpContext (z. B. IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Wie bei allen Empfehlungen treffen Sie möglicherweise auf Situationen, in denen eine Empfehlung ignoriert werden muss.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Es gibt nur wenige Ausnahmen — die sich meistens auf besondere Fälle innerhalb des Frameworks beziehen.Exceptions are rare—mostly special cases within the framework itself.

Dependency Injection stellt eine Alternative zu statischen bzw. globalen Objektzugriffsmustern dar.DI is an alternative to static/global object access patterns. Sie werden keinen Nutzen aus der Dependency Injection ziehen können, wenn Sie diese mit dem Zugriff auf statische Objekte kombinieren.You may not be able to realize the benefits of DI if you mix it with static object access.

Zusätzliche RessourcenAdditional resources