Wstrzykiwanie zależności w ASP.NET CoreDependency injection in ASP.NET Core

Steve Kowalski, Scott Addiei Luke LathamBy Steve Smith, Scott Addie, and Luke Latham

ASP.NET Core obsługuje wzorzec projektowania oprogramowania dla iniekcji zależności, który jest techniką do osiągnięcia niewersji kontroli (IOC) między klasami i ich zależnościami.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.

Aby uzyskać więcej informacji specyficznych dla iniekcji zależności w kontrolerach MVC, zobacz Wstrzykiwanie zależności do kontrolerów w programie ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Wstrzykiwanie zależności do kontrolerów w programie ASP.NET Core.

Wyświetlanie lub pobieranie przykładowego kodu (sposobu pobierania)View or download sample code (how to download)

Przegląd iniekcji zależnościOverview of dependency injection

Zależność jest dowolnym obiektem, który jest wymagany przez inny obiekt.A dependency is any object that another object requires. Zapoznaj się MyDependency WriteMessage z następującą klasą, korzystając z metody, na której zależą inne klasy w aplikacji: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);
    }
}

Wystąpienie MyDependency klasy można utworzyć w celu WriteMessage udostępnienia metody klasie.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. Klasa jest zależnością IndexModelklasy: MyDependencyThe 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.");
    }
}

Klasa tworzy i bezpośrednio zależy MyDependency od wystąpienia.The class creates and directly depends on the MyDependency instance. Zależności kodu (takie jak w poprzednim przykładzie) są problematyczne i należy je unikać z następujących powodów:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Aby zastąpić MyDependency inną implementacją, należy zmodyfikować klasę.To replace MyDependency with a different implementation, the class must be modified.
  • Jeśli MyDependency ma zależności, muszą one być skonfigurowane przez klasę.If MyDependency has dependencies, they must be configured by the class. W dużym projekcie z wieloma klasami MyDependency, w zależności od tego, kod konfiguracji jest rozmieszczany w całej aplikacji.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Ta implementacja jest trudna do testowania jednostkowego.This implementation is difficult to unit test. Aplikacja powinna używać klasy imitacji lub zastępczej MyDependency , która nie jest możliwa w przypadku tego podejścia.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

Iniekcja zależności eliminuje te problemy w następujący sposób:Dependency injection addresses these problems through:

  • Użycie interfejsu lub klasy bazowej do abstrakcyjnej implementacji zależności.The use of an interface or base class to abstract the dependency implementation.
  • Rejestracja zależności w kontenerze usługi.Registration of the dependency in a service container. ASP.NET Core udostępnia wbudowany kontener usługi, IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. Usługi są zarejestrowane w Startup.ConfigureServices metodzie aplikacji.Services are registered in the app's Startup.ConfigureServices method.
  • Iniekcja usługi do konstruktora klasy, w której jest używana.Injection of the service into the constructor of the class where it's used. Struktura przejmuje odpowiedzialność za utworzenie wystąpienia zależności i jego likwidację, gdy nie jest już potrzebny.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

W przykładowej aplikacji IMyDependency interfejs definiuje metodę dostarczaną przez usługę do aplikacji:In the sample app, the IMyDependency interface defines a method that the service provides to the app:

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

Ten interfejs jest implementowany przez konkretny typ MyDependency: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);
    }
}

MyDependencyILogger<TCategoryName> żąda w konstruktorze.MyDependency requests an ILogger<TCategoryName> in its constructor. Użycie iniekcji zależności w łańcuchu nie jest nietypowe.It's not unusual to use dependency injection in a chained fashion. Każda żądana zależność z kolei żąda własnych zależności.Each requested dependency in turn requests its own dependencies. Kontener rozwiązuje zależności w grafie i zwraca w pełni rozwiązane usługi.The container resolves the dependencies in the graph and returns the fully resolved service. Zestaw zbiorczy zależności, które muszą zostać rozwiązane, jest zwykle nazywany drzewem zależności, wykresem zależnościlub wykresem obiektów.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependencyi ILogger<TCategoryName> musi być zarejestrowany w kontenerze usługi.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependencyjest zarejestrowany w Startup.ConfigureServices.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName>jest zarejestrowany przez infrastrukturę abstrakcji rejestrowania, więc jest to Usługa udostępniona przez platformę zarejestrowana domyślnie przez platformę.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

Kontener jest rozpoznawany ILogger<TCategoryName> przez wykorzystanie (rodzajowe) otwartych typów, eliminując konieczność zarejestrowania każdego (rodzajowego) konstruowanego typu: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>));

W przykładowej aplikacji IMyDependency usługa jest zarejestrowana w konkretnym typie MyDependency.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. Rejestracja zakresów okresu istnienia usługi do okresu istnienia pojedynczego żądania.The registration scopes the service lifetime to the lifetime of a single request. Okresy istnienia usługi zostały opisane w dalszej części tego tematu.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>();
}

Uwaga

Każda services.Add{SERVICE_NAME} Metoda rozszerzenia dodaje usługi (i potencjalnie konfiguruje).Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Na przykład services.AddMvc() dodaje usługi Razor Pages i MVC.For example, services.AddMvc() adds the services Razor Pages and MVC require. Zalecamy, aby aplikacje były zgodne z tą konwencją.We recommended that apps follow this convention. Umieść metody rozszerzające w przestrzeni nazw Microsoft. Extensions. DependencyInjection , aby hermetyzować grupy rejestracji usług.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Jeśli Konstruktor usługi wymaga wbudowanego typu, takiego jak string,, typ można wstrzyknąć przy użyciu konfiguracji lub wzorca opcji: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
    }

    ...
}

Wystąpienie usługi jest wymagane za pośrednictwem konstruktora klasy, w której jest używana usługa i jest przypisywana do pola prywatnego.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. To pole jest używane w celu uzyskania dostępu do usługi w zależności od potrzeb w całej klasie.The field is used to access the service as necessary throughout the class.

W przykładowej aplikacji IMyDependency wystąpienie jest wymagane i używane do wywołania WriteMessage metody usługi: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.");
    }
}

Usługi udostępniane przez platformęFramework-provided services

Startup.ConfigureServices Metoda jest odpowiedzialna za Definiowanie usług używanych przez aplikację, w tym funkcji platformy, takich jak Entity Framework Core i 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. Początkowo, IServiceCollection ConfigureServices pod warunkiem, że zostały zdefiniowane następujące usługi (w zależności od konfiguracji hosta):Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on how the host was configured):

Typ usługiService Type Okres istnieniaLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory PrzejścioweTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetime PojedynczegoSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment PojedynczegoSingleton
Microsoft.AspNetCore.Hosting.IStartup PojedynczegoSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter PrzejścioweTransient
Microsoft.AspNetCore.Hosting.Server.IServer PojedynczegoSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory PrzejścioweTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> PojedynczegoSingleton
Microsoft.Extensions.Logging.ILoggerFactory PojedynczegoSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider PojedynczegoSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> PrzejścioweTransient
Microsoft.Extensions.Options.IOptions<TOptions> PojedynczegoSingleton
System.Diagnostics.DiagnosticSource PojedynczegoSingleton
System.Diagnostics.DiagnosticListener PojedynczegoSingleton

Gdy dostępna jest Metoda rozszerzenia kolekcji usług w celu zarejestrowania usługi (i zależnych od niej usług, jeśli jest to wymagane), Konwencja ma używać Add{SERVICE_NAME} pojedynczej metody rozszerzającej do rejestrowania wszystkich usług wymaganych przez tę usługę.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. Poniższy kod stanowi przykład dodawania dodatkowych usług do kontenera przy użyciu metod rozszerzających <AddDbContext TContext >, AddIdentityCorei 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();
}

Aby uzyskać więcej informacji, zapoznaj się z ServiceCollection klasą w dokumentacji interfejsu API.For more information, see the ServiceCollection class in the API documentation.

Okresy istnienia usługiService lifetimes

Wybierz odpowiedni okres istnienia dla każdej zarejestrowanej usługi.Choose an appropriate lifetime for each registered service. Usługi ASP.NET Core można skonfigurować przy użyciu następujących okresów istnienia:ASP.NET Core services can be configured with the following lifetimes:

PrzejścioweTransient

Przejściowe usługiAddTransientokresu istnienia są tworzone za każdym razem, gdy zażądają one kontenera usług.Transient lifetime services (AddTransient) are created each time they're requested from the service container. Ten okres istnienia działa najlepiej w przypadku lekkich i bezstanowych usług.This lifetime works best for lightweight, stateless services.

ZakresieScoped

Usługi okresu istnienia wAddScopedzakresie () są tworzone raz dla każdego żądania klienta (połączenie).Scoped lifetime services (AddScoped) are created once per client request (connection).

Ostrzeżenie

W przypadku korzystania z usługi w zakresie w oprogramowaniu pośredniczącym należy wstrzyknąć usługę do Invoke metody InvokeAsync lub.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Nie wprowadzaj przez iniekcję konstruktora, ponieważ wymusza ona zachowanie usługi jako pojedynczej.Don't inject via constructor injection because it forces the service to behave like a singleton. Aby uzyskać więcej informacji, zobacz Napisz niestandardowe oprogramowanie pośredniczące ASP.NET Core.For more information, see Napisz niestandardowe oprogramowanie pośredniczące ASP.NET Core.

PojedynczegoSingleton

Pojedyncze usługi okresu istnienia (AddSingleton) są tworzone podczas pierwszego żądania (lub gdy Startup.ConfigureServices jest uruchamiany, a wystąpienie jest określone przy rejestracji usługi).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). Każde kolejne żądanie używa tego samego wystąpienia.Every subsequent request uses the same instance. Jeśli aplikacja wymaga pojedynczych zachowań, zaleca się, aby można było zarządzać okresem istnienia usługi przez kontener usługi.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Nie Wdrażaj wzorca projektu singleton i podaj kod użytkownika, aby zarządzać okresem istnienia obiektu w klasie.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

Ostrzeżenie

Rozwiązanie usługi o określonym zakresie z pojedynczej jest niebezpieczne.It's dangerous to resolve a scoped service from a singleton. Może to spowodować, że usługa będzie mieć nieprawidłowy stan podczas przetwarzania kolejnych żądań.It may cause the service to have incorrect state when processing subsequent requests.

Metody rejestracji usługService registration methods

Każda metoda rozszerzenia rejestracji usługi oferuje przeciążenia, które są przydatne w określonych scenariuszach.Each service registration extension method offers overloads that are useful in specific scenarios.

MetodaMethod AutomatyczneAutomatic
objectobject
myśldisposal
WielokrotneMultiple
implementacjeimplementations
Przekaż argumentyPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Przykład:Example:
services.AddScoped<IMyDep, MyDep>();
TakYes YesYes NieNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Przykłady:Examples:
services.AddScoped<IMyDep>(sp => new MyDep());
services.AddScoped<IMyDep>(sp => new MyDep("A string!"));
TakYes YesYes TakYes
Add{LIFETIME}<{IMPLEMENTATION}>()
Przykład:Example:
services.AddScoped<MyDep>();
TakYes NieNo NieNo
Add{LIFETIME}<{SERVICE}>(new {IMPLEMENTATION})
Przykłady:Examples:
services.AddScoped<IMyDep>(new MyDep());
services.AddScoped<IMyDep>(new MyDep("A string!"));
NieNo YesYes TakYes
Add{LIFETIME}(new {IMPLEMENTATION})
Przykłady:Examples:
services.AddScoped(new MyDep());
services.AddScoped(new MyDep("A string!"));
NieNo NieNo TakYes

Aby uzyskać więcej informacji na temat usuwania typów, zobacz sekcję dotyczącą usuwania usług .For more information on type disposal, see the Disposal of services section. Typowym scenariuszem dla wielu implementacji jest imitacja typów do testowania.A common scenario for multiple implementations is mocking types for testing.

TryAdd{LIFETIME}Metody rejestrują usługę tylko wtedy, gdy nie zarejestrowano jeszcze implementacji.TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

W poniższym przykładzie pierwszy wiersz rejestruje MyDependency. IMyDependencyIn the following example, the first line registers MyDependency for IMyDependency. Drugi wiersz nie ma wpływu, ponieważ IMyDependency ma już zarejestrowaną implementację: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>();

Aby uzyskać więcej informacji, zobacz:For more information, see:

Metody TryAddEnumerable (servicedescriptor) rejestrują usługę tylko wtedy, gdy nie istnieje jeszcze implementacja tego samego typu.TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. Wiele usług jest rozpoznawanych IEnumerable<{SERVICE}>za pośrednictwem.Multiple services are resolved via IEnumerable<{SERVICE}>. Podczas rejestrowania usług deweloper chce tylko dodać wystąpienie, jeśli jeden z tych samych typów nie został jeszcze dodany.When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. Ogólnie rzecz biorąc, ta metoda jest używana przez autorów biblioteki, aby uniknąć rejestrowania dwóch kopii wystąpienia w kontenerze.Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

W poniższym przykładzie pierwszy wiersz rejestruje MyDep. IMyDep1In the following example, the first line registers MyDep for IMyDep1. Drugi wiersz rejestruje MyDep. IMyDep2The second line registers MyDep for IMyDep2. Trzeci wiersz nie działa, ponieważ IMyDep1 ma już zarejestrowana MyDepimplementacja: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>());

Zachowanie iniekcji konstruktoraConstructor injection behavior

Usługi mogą być rozwiązywane przez dwa mechanizmy:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities– Zezwala na tworzenie obiektów bez rejestracji usługi w kontenerze iniekcji zależności.ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilitiesjest używany z abstrakcjami dostępnymi dla użytkowników, takimi jak pomocnicy tagów, kontrolery MVC i powiązania modelu.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Konstruktory mogą akceptować argumenty, które nie są dostarczane przez iniekcję zależności, ale argumenty muszą przypisywać wartości domyślne.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Gdy usługi są rozwiązane IServiceProvider przez ActivatorUtilitieslub, iniekcja konstruktora wymaga konstruktora publicznego .When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Gdy usługi są rozwiązane ActivatorUtilitiesprzez, iniekcja konstruktora wymaga, aby istnieje tylko jeden odpowiedni Konstruktor.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Przeciążenia konstruktora są obsługiwane, ale może istnieć tylko jedno Przeciążenie, którego argumenty mogą być spełnione przez iniekcję zależności.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Konteksty Entity FrameworkEntity Framework contexts

Konteksty Entity Framework są zazwyczaj dodawane do kontenera usługi przy użyciu okresu istnienia zakresu , ponieważ operacje bazy danych aplikacji sieci Web są zwykle ograniczone do żądania klienta.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. Domyślny okres istnienia jest objęty zakresem, jeśli okres istnienia nie jest określony przez Przeciążenie <AddDbContext TContext > Overload podczas rejestrowania kontekstu bazy danych.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. Usługi danego okresu istnienia nie powinny używać kontekstu bazy danych z krótszym okresem istnienia niż usługa.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Opcje okresu istnienia i rejestracjiLifetime and registration options

Aby zademonstrować różnicę między opcjami czasu istnienia i rejestracji, należy wziąć pod uwagę następujące interfejsy, które reprezentują zadania jako operacje OperationIdz unikatowym identyfikatorem.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. W zależności od sposobu skonfigurowania okresu istnienia usługi operacji dla następujących interfejsów kontener zawiera to samo lub inne wystąpienie usługi, gdy żądanie klasy: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
{
}

Interfejsy są zaimplementowane w Operation klasie.The interfaces are implemented in the Operation class. Operation Konstruktor generuje identyfikator GUID, jeśli nie został podany: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; }
}

Zarejestrowano, który zależy od poszczególnych Operation typów. OperationServiceAn OperationService is registered that depends on each of the other Operation types. Gdy OperationService żądanie jest wykonywane za pośrednictwem iniekcji zależności, otrzymuje nowe wystąpienie każdej usługi lub istniejące wystąpienie na podstawie okresu istnienia usługi zależnej.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.

  • Gdy usługi przejściowe są tworzone po zażądaniu kontenera, OperationId IOperationTransient OperationId usługa różni się od 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. OperationServiceodbiera nowe wystąpienie IOperationTransient klasy.OperationService receives a new instance of the IOperationTransient class. Nowe wystąpienie daje inną OperationIdwartość.The new instance yields a different OperationId.
  • Gdy usługi w zakresie są tworzone dla każdego żądania klienta, OperationId IOperationScoped usługa jest taka sama jak OperationService w ramach żądania klienta.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. W przypadku żądań klientów obie te usługi współdzielą inną OperationId wartość.Across client requests, both services share a different OperationId value.
  • Gdy pojedyncze i pojedyncze usługi wystąpienia są tworzone raz i używane przez wszystkie żądania klientów i wszystkie usługi, OperationId jest to stała między wszystkimi żądaniami obsługi.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; }
}

W Startup.ConfigureServicesprogramie każdy typ jest dodawany do kontenera zgodnie z jego nazwanym okresem istnienia: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>();
}

Usługa używa określonego wystąpienia o znanym Guid.Emptyidentyfikatorze. IOperationSingletonInstanceThe IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. Jest to jasne, gdy ten typ jest używany (jego identyfikator GUID to wszystkie zera).It's clear when this type is in use (its GUID is all zeroes).

Przykładowa aplikacja pokazuje okresy istnienia obiektów w ramach poszczególnych żądań i między nimi.The sample app demonstrates object lifetimes within and between individual requests. Przykładowa aplikacja IndexModel wysyła żądania każdego IOperation rodzaju typu i OperationService.The sample app's IndexModel requests each kind of IOperation type and the OperationService. Następnie na stronie zostaną wyświetlone wszystkie OperationId wartości klasy modelu strony i usługi za pomocą przypisań właściwości: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.");
    }
}

Dwa następujące dane wyjściowe pokazują wyniki dwóch żądań:Two following output shows the results of two requests:

Pierwsze żądanie:First request:

Operacje kontrolera:Controller operations:

Przejściowy: d233e165-f417-469B-A866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Zakresie 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Pojedynczego 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Np 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationServiceskładowaOperationService operations:

Przejściowy: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Zakresie 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Pojedynczego 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Np 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Drugie żądanie:Second request:

Operacje kontrolera:Controller operations:

Przejściowy: b63bd538-0a37-4FF1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Zakresie 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Pojedynczego 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Np 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationServiceskładowaOperationService operations:

Przejściowy: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Zakresie 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Pojedynczego 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Np 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Zauważ, że wartości OperationId różnią się w zależności od żądania i między żądaniami:Observe which of the OperationId values vary within a request and between requests:

  • Obiekty przejściowe są zawsze różne.Transient objects are always different. Wartość przejściowa OperationId dla pierwszego i drugiego żądania klienta różni się OperationService w zależności od operacji i między żądaniami klientów.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Nowe wystąpienie jest dostarczane do każdego żądania obsługi i żądania klienta.A new instance is provided to each service request and client request.
  • Obiekty w zakresie są takie same w obrębie żądania klienta, ale różnią się w zależności od żądań klientów.Scoped objects are the same within a client request but different across client requests.
  • Pojedyncze obiekty są takie same dla każdego obiektu i każdego żądania, niezależnie od tego, Operation czy wystąpienie jest dostarczone Startup.ConfigureServicesw.Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

Wywoływanie usług z głównegoCall services from main

Utwórz element IServiceScope with IServiceScopeFactory. isscope , aby rozwiązać usługę objętą zakresem w zakresie aplikacji.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Takie podejście jest przydatne do uzyskiwania dostępu do usługi w zakresie podczas uruchamiania do uruchamiania zadań inicjowania.This approach is useful to access a scoped service at startup to run initialization tasks. Poniższy przykład pokazuje, jak uzyskać kontekst dla MyScopedService: Program.MainThe 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();
}

Weryfikacja zakresuScope validation

Gdy aplikacja jest uruchomiona w środowisku programistycznym, domyślny dostawca usług sprawdza, czy:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Usługi w zakresie nie są bezpośrednio lub pośrednio rozpoznawane przez dostawcę usług głównych.Scoped services aren't directly or indirectly resolved from the root service provider.
  • Usługi w zakresie nie są bezpośrednio lub pośrednio wstrzykiwane do pojedynczych.Scoped services aren't directly or indirectly injected into singletons.

Dostawca usług głównych jest tworzony, gdy BuildServiceProvider jest wywoływany.The root service provider is created when BuildServiceProvider is called. Okres istnienia dostawcy usług głównych odnosi się do okresu istnienia aplikacji/serwera, gdy dostawca zaczyna się od aplikacji i jest usuwany po zamknięciu aplikacji.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.

Usługi o określonym zakresie są usuwane przez kontener, który go utworzył.Scoped services are disposed by the container that created them. Jeśli w kontenerze głównym zostanie utworzona usługa o określonym zakresie, okres istnienia usługi zostanie skutecznie podwyższony do pojedynczej, ponieważ jest usuwany tylko przez kontener główny, gdy aplikacja/serwer jest wyłączony.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. Sprawdzanie poprawności zakresów usług przechwytuje te BuildServiceProvider sytuacje, gdy jest wywoływana.Validating service scopes catches these situations when BuildServiceProvider is called.

Aby uzyskać więcej informacji, zobacz ASP.NET Core hosta sieci Web.For more information, see ASP.NET Core hosta sieci Web.

Usługi żądaniaRequest Services

Usługi dostępne w ramach żądania HttpContext ASP.NET Core są udostępniane za pośrednictwem kolekcji HttpContext. RequestServices .The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Usługi żądania reprezentują usługi skonfigurowane i żądane jako część aplikacji.Request Services represent the services configured and requested as part of the app. Gdy obiekty określają zależności, są one spełnione przez typy Znalezione w RequestServices, nie. ApplicationServicesWhen the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Ogólnie rzecz biorąc aplikacja nie powinna używać tych właściwości bezpośrednio.Generally, the app shouldn't use these properties directly. Zamiast tego Zażądaj typów, które klasy wymagają za pośrednictwem konstruktorów klas, i zezwól na wstrzyknięcie zależności przez strukturę.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. To daje klasy, które są łatwiejsze do przetestowania.This yields classes that are easier to test.

Uwaga

Preferuj żądania zależności jako parametry konstruktora, aby uzyskać dostęp RequestServices do kolekcji.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Projektowanie usług do iniekcji zależnościDesign services for dependency injection

Najlepsze rozwiązania:Best practices are to:

  • Projektowanie usług do korzystania z iniekcji zależności w celu uzyskania ich zależności.Design services to use dependency injection to obtain their dependencies.
  • Unikaj stanowych wywołań metod statycznych.Avoid stateful, static method calls.
  • Unikaj bezpośredniego tworzenia wystąpień klas zależnych w ramach usług.Avoid direct instantiation of dependent classes within services. Bezpośrednie utworzenie wystąpienia Couples kod do konkretnej implementacji.Direct instantiation couples the code to a particular implementation.
  • Twórz klasy aplikacji małymi, dobrze i łatwo przetestowane.Make app classes small, well-factored, and easily tested.

Jeśli Klasa prawdopodobnie ma zbyt wiele zawstrzykiwanych zależności, zwykle jest to znak, że Klasa ma zbyt wiele obowiązków i narusza zasadę pojedynczej odpowiedzialności (SRP).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). Próba refaktoryzacji klasy przez przeniesienie niektórych jej obowiązków do nowej klasy.Attempt to refactor the class by moving some of its responsibilities into a new class. Należy pamiętać, że klasy Razor Pages modelu strony i kontrolery MVC powinny skupić się na problemach z interfejsem użytkownika.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Reguły biznesowe i szczegóły implementacji dostępu do danych powinny być przechowywane w klasach odpowiednich do tych oddzielnych obaw.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Usuwanie usługDisposal of services

Kontener wywołuje Dispose typy, które IDisposable tworzy.The container calls Dispose for the IDisposable types it creates. Jeśli wystąpienie jest dodawane do kontenera przez kod użytkownika, nie zostanie usunięte automatycznie.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());
}

Zastępowanie kontenera usług domyślnychDefault service container replacement

Wbudowany kontener usługi jest przeznaczony do obsługi potrzeb platformy i większości aplikacji konsumenckich.The built-in service container is meant to serve the needs of the framework and most consumer apps. Zalecamy użycie wbudowanego kontenera, chyba że potrzebna jest konkretna funkcja, której nie obsługuje.We recommend using the built-in container unless you need a specific feature that it doesn't support. Niektóre funkcje obsługiwane w kontenerach innych firm nie znajdują się w kontenerze wbudowanym:Some of the features supported in 3rd party containers not found in the built-in container:

  • Iniekcja właściwościProperty injection
  • Iniekcja oparta na nazwieInjection based on name
  • Kontenery podrzędneChild containers
  • Niestandardowe zarządzanie okresem istnieniaCustom lifetime management
  • Func<T>Obsługa inicjowania z opóźnieniemFunc<T> support for lazy initialization

Zobacz plik Readme.MD iniekcji zależności , aby uzyskać listę niektórych kontenerów, które obsługują karty sieciowe.See the Dependency Injection readme.md file for a list of some of the containers that support adapters.

Poniższy przykład zastępuje wbudowany kontener z Autofac:The following sample replaces the built-in container with Autofac:

  • Zainstaluj odpowiednie pakiety kontenerów:Install the appropriate container package(s):

  • Skonfigurowanie kontenera w programie Startup.ConfigureServices i IServiceProviderzwrócenie: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);
    }
    

    Aby użyć kontenera innej firmy, Startup.ConfigureServices należy zwrócić IServiceProviderwartość.To use a 3rd party container, Startup.ConfigureServices must return IServiceProvider.

  • Skonfiguruj Autofac w DefaultModule:Configure Autofac in DefaultModule:

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

W czasie wykonywania Autofac jest używany do rozpoznawania typów i wtryskiwania zależności.At runtime, Autofac is used to resolve types and inject dependencies. Aby dowiedzieć się więcej o korzystaniu z programu Autofac z ASP.NET Core, zobacz dokumentację Autofac.To learn more about using Autofac with ASP.NET Core, see the Autofac documentation.

Bezpieczeństwo wątkówThread safety

Twórz bezpieczne dla wątków usługi pojedyncze.Create thread-safe singleton services. Jeśli usługa singleton ma zależność od przejściowej usługi, usługa przejściowa może również wymagać bezpieczeństwa wątku, w zależności od tego, w jaki sposób jest używana przez pojedyncze.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.

Metoda fabryki pojedynczej usługi, taka jak drugi argument dla AddSingleton <TService > (IServiceCollection, Func<IServiceProvider, TService >), nie musi być bezpieczna wątkowo.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. Podobnie jak w przypadkustatickonstruktora typu (), gwarantowane jest wywoływanie jednokrotne przez pojedynczy wątek.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

ZaleceniaRecommendations

  • async/awaitRozpoznawanie Task usług na podstawie nie jest obsługiwane.async/await and Task based service resolution is not supported. C#nie obsługuje konstruktorów asynchronicznych; w związku z tym zalecany wzorzec polega na użyciu metod asynchronicznych po synchronicznym rozpoznaniu usługi.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Unikaj przechowywania danych i konfiguracji bezpośrednio w kontenerze usługi.Avoid storing data and configuration directly in the service container. Na przykład koszyk użytkownika nie powinien być zazwyczaj dodawany do kontenera usługi.For example, a user's shopping cart shouldn't typically be added to the service container. Konfiguracja powinna używać wzorca opcji.Configuration should use the options pattern. Podobnie należy unikać obiektów "posiadaczy danych", które istnieją tylko w celu zezwolenia na dostęp do innego obiektu.Similarly, avoid "data holder" objects that only exist to allow access to some other object. Lepiej jest zażądać rzeczywistego elementu za pośrednictwem DI.It's better to request the actual item via DI.

  • Należy unikać statycznego dostępu do usług (na przykład statycznego wpisywania IApplicationBuilder. ApplicationServices do użytku w innym miejscu).Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • Unikaj używania wzorca lokalizatora usługi.Avoid using the service locator pattern. Na przykład nie wywołuj GetService , aby uzyskać wystąpienie usługi, gdy można użyć di zamiast:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    PrawidłowyIncorrect:

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

    Poprawne:Correct:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Inna odmiana lokalizatora usługi, aby uniknąć, wprowadza fabrykę, która rozwiązuje zależności w czasie wykonywania.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Obie te praktyki mieszają się z niewersjami strategii kontroli .Both of these practices mix Inversion of Control strategies.

  • Unikaj dostępu statycznego do HttpContext (na przykład IHttpContextAccessor. HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Podobnie jak w przypadku wszystkich zestawów zaleceń, mogą wystąpić sytuacje, w których ignorowanie zalecenia jest wymagane.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Wyjątki są rzadko—w większości specjalnych przypadków w ramach samej struktury.Exceptions are rare—mostly special cases within the framework itself.

DI jest alternatywą dla wzorców dostępu do obiektów static/Global.DI is an alternative to static/global object access patterns. Możesz nie być w stanie korzystać z zalet programu DI w przypadku jego mieszania z dostępem do obiektów statycznych.You may not be able to realize the benefits of DI if you mix it with static object access.

Dodatkowe zasobyAdditional resources