Vkládání závislostí v ASP.NET CoreDependency injection in ASP.NET Core

Od Kirka Larkin, Steve Smith, Scott Addiea Brandon DahlerBy Kirk Larkin, Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core podporuje vzor návrhu softwaru pro vkládání závislostí (DI), což je technika pro dosažení inverze ovládacího prvku (IOC) mezi třídami a jejich závislostmi.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.

Další informace, které jsou specifické pro vkládání závislostí v rámci řadičů MVC, najdete v tématu Vkládání závislostí do řadičů v ASP.NET Core .For more information specific to dependency injection within MVC controllers, see Vkládání závislostí do řadičů v ASP.NET Core.

Další informace o vkládání závislostí z možností naleznete v tématu Vzor možností v ASP.NET Core .For more information on dependency injection of options, see Vzor možností v ASP.NET Core.

Zobrazit nebo stáhnout ukázkový kód (Jak stáhnout)View or download sample code (how to download)

Přehled injektáže závislostiOverview of dependency injection

Závislost je objekt, na kterém je závislý jiný objekt.A dependency is an object that another object depends on. Projděte si následující MyDependency třídu s WriteMessage metodou, na které jsou závislé jiné třídy:Examine the following MyDependency class with a WriteMessage method that other classes depend on:

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

Třída může vytvořit instanci MyDependency třídy, aby bylo možné využít její WriteMessage metodu.A class can create an instance of the MyDependency class to make use of its WriteMessage method. V následujícím příkladu MyDependency je třída závislostí IndexModel třídy:In the following example, the MyDependency class is a dependency of the IndexModel class:

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

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet created this message.");
    }
}

Třída vytvoří a přímo závisí na MyDependency třídě.The class creates and directly depends on the MyDependency class. Závislosti kódu, jako v předchozím příkladu, jsou problematické a je třeba se jim vyhnout z následujících důvodů:Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • Chcete-li nahradit MyDependency jinou implementací, IndexModel Třída musí být upravena.To replace MyDependency with a different implementation, the IndexModel class must be modified.
  • Pokud MyDependency má závislosti, musí být také nakonfigurovány IndexModel třídou.If MyDependency has dependencies, they must also be configured by the IndexModel class. Ve velkém projektu s více třídami, v závislosti na tom MyDependency , je kód konfigurace rozptýlen napříč aplikací.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Tato implementace je obtížná pro testování částí.This implementation is difficult to unit test. Aplikace by měla používat třídu typu "nebo zástupné procedury" MyDependency , což není u tohoto přístupu možné.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

Vkládání závislostí řeší tyto problémy prostřednictvím:Dependency injection addresses these problems through:

  • Použití rozhraní nebo základní třídy k abstrakci implementace závislosti.The use of an interface or base class to abstract the dependency implementation.
  • Registrace závislosti v kontejneru služby.Registration of the dependency in a service container. ASP.NET Core poskytuje integrovaný kontejner služeb IServiceProvider .ASP.NET Core provides a built-in service container, IServiceProvider. Služby jsou obvykle registrovány v metodě aplikace Startup.ConfigureServices .Services are typically registered in the app's Startup.ConfigureServices method.
  • Vložení služby do konstruktoru třídy, kde se používá.Injection of the service into the constructor of the class where it's used. Rozhraní přebírá zodpovědnost za vytvoření instance závislosti a její odstranění, když už ji nepotřebujete.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

V ukázkové aplikaci IMyDependency definuje rozhraní WriteMessage metodu:In the sample app, the IMyDependency interface defines the WriteMessage method:

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

Toto rozhraní je implementováno konkrétním typem MyDependency :This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

Ukázková aplikace registruje IMyDependency službu pomocí konkrétního typu MyDependency .The sample app registers the IMyDependency service with the concrete type MyDependency. AddScopedMetoda registruje službu s rozsahem životnosti, životností jediného požadavku.The AddScoped method registers the service with a scoped lifetime, the lifetime of a single request. Doby platnosti služeb jsou popsány dále v tomto tématu.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();

    services.AddRazorPages();
}

V ukázkové aplikaci IMyDependency se vyžádá služba a použije se k volání WriteMessage metody:In the sample app, the IMyDependency service is requested and used to call the WriteMessage method:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

Pomocí vzoru DI se kontroler:By using the DI pattern, the controller:

  • Nepoužívá konkrétní typ MyDependency , pouze rozhraní, které IMyDependency implementuje.Doesn't use the concrete type MyDependency, only the IMyDependency interface it implements. Díky tomu je možné snadno změnit implementaci, kterou kontroler používá bez změny kontroleru.That makes it easy to change the implementation that the controller uses without modifying the controller.
  • Nevytvoří instanci, která je MyDependency vytvořena kontejnerem di.Doesn't create an instance of MyDependency, it's created by the DI container.

Implementaci IMyDependency rozhraní lze zlepšit pomocí integrovaného protokolovacího rozhraní API:The implementation of the IMyDependency interface can be improved by using the built-in logging API:

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

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

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

Aktualizovaná ConfigureServices Metoda registruje novou IMyDependency implementaci:The updated ConfigureServices method registers the new IMyDependency implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency2>();

    services.AddRazorPages();
}

MyDependency2 závisí na ILogger<TCategoryName> tom, které požadavky v konstruktoru.MyDependency2 depends on ILogger<TCategoryName>, which it requests in the constructor. ILogger<TCategoryName> je služba poskytovaná rozhraním.ILogger<TCategoryName> is a framework-provided service.

Není neobvyklé používat vkládání závislostí v zřetězeném způsobem.It's not unusual to use dependency injection in a chained fashion. Každá požadovaná závislost zase vyžádá své vlastní závislosti.Each requested dependency in turn requests its own dependencies. Kontejner vyřeší závislosti v grafu a vrátí plně přeloženou službu.The container resolves the dependencies in the graph and returns the fully resolved service. Kolektivní sada závislostí, které je třeba vyřešit, se obvykle označuje jako strom závislosti, graf závislostinebo graf objektů.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

Kontejner se překládá ILogger<TCategoryName> tím, že využije (Obecné) otevřené typya eliminuje nutnost registrovat každý (obecný) konstruovaný typ.The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type.

V terminologii injektáže závislostí služba:In dependency injection terminology, a service:

  • Je obvykle objekt, který poskytuje službu jiným objektům, jako je například IMyDependency služba.Is typically an object that provides a service to other objects, such as the IMyDependency service.
  • Nesouvisí s webovou službou, i když služba může používat webovou službu.Is not related to a web service, although the service may use a web service.

Rozhraní poskytuje robustní systém protokolování .The framework provides a robust logging system. IMyDependencyImplementace uvedené v předchozích příkladech byly zapsány k demonstraci základního di, nikoli k implementaci protokolování.The IMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. Většina aplikací by neměla potřebovat zapisovat protokolovací nástroje.Most apps shouldn't need to write loggers. Následující kód ukazuje použití výchozího protokolování, které nevyžaduje registraci žádné služby v nástroji ConfigureServices :The following code demonstrates using the default logging, which doesn't require any services to be registered in ConfigureServices:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Pomocí předchozího kódu není nutné aktualizovat ConfigureServices , protože protokolování je poskytováno rozhraním.Using the preceding code, there is no need to update ConfigureServices, because logging is provided by the framework.

Služby vložené do spuštěníServices injected into Startup

Služby lze vložit do Startup konstruktoru a do Startup.Configure metody.Services can be injected into the Startup constructor and the Startup.Configure method.

StartupPři použití obecného hostitele () mohou být do konstruktoru vloženy pouze následující služby IHostBuilder :Only the following services can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

Kterákoli služba zaregistrovaná v kontejneru DI může být vložená do Startup.Configure metody:Any service registered with the DI container can be injected into the Startup.Configure method:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    ...
}

Další informace najdete v tématu Spuštění aplikace v ASP.NET Core a Konfigurace přístupu při spuštění.For more information, see Spuštění aplikace v ASP.NET Core and Access configuration in Startup.

Registrace skupin služeb s metodami rozšířeníRegister groups of services with extension methods

Rozhraní ASP.NET Core Framework používá konvenci pro registraci skupiny souvisejících služeb.The ASP.NET Core framework uses a convention for registering a group of related services. Konvence je určena k Add{GROUP_NAME} registraci všech služeb vyžadovaných funkcí rozhraní pomocí jediné metody rozšíření.The convention is to use a single Add{GROUP_NAME} extension method to register all of the services required by a framework feature. Například <Microsoft. Extensions. DependencyInjection. MvcServiceCollectionExtensions. AddControllers> rozšiřující metoda registruje služby požadované pro řadiče MVC.For example, the <Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers> extension method registers the services required for MVC controllers.

Následující kód je vygenerován Razor šablonou stránky pomocí individuálních uživatelských účtů a ukazuje, jak přidat další služby do kontejneru pomocí metod rozšíření AddDbContext a AddDefaultIdentity :The following code is generated by the Razor Pages template using individual user accounts and shows how to add additional services to the container using the extension methods AddDbContext and AddDefaultIdentity:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Vezměte v úvahu následující ConfigureServices metodu, která registruje služby a nakonfiguruje možnosti:Consider the following ConfigureServices method, which registers services and configures options:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
        Configuration.GetSection(PositionOptions.Position));
    services.Configure<ColorOptions>(
        Configuration.GetSection(ColorOptions.Color));

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddScoped<IMyDependency2, MyDependency2>();

    services.AddRazorPages();
}

Související skupiny registrací lze přesunout do metody rozšíření pro registraci služeb.Related groups of registrations can be moved to an extension method to register services. Například konfigurační služby jsou přidány do následující třídy:For example, the configuration services are added to the following class:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }
    }
}

Zbývající služby jsou registrovány v podobné třídě.The remaining services are registered in a similar class. Následující ConfigureServices Metoda používá nové metody rozšíření k registraci služeb:The following ConfigureServices method uses the new extension methods to register the services:

public void ConfigureServices(IServiceCollection services)
{
    services.AddConfig(Configuration)
            .AddMyDependencyGroup();

    services.AddRazorPages();
}

Poznámka: Každá services.Add{GROUP_NAME} metoda rozšíření přidá a případně nakonfiguruje služby.Note: Each services.Add{GROUP_NAME} extension method adds and potentially configures services. Například AddControllersWithViews Přidání řadičů služby MVC s zobrazeními vyžaduje a AddRazorPages přidá služby Razor Pages vyžaduje.For example, AddControllersWithViews adds the services MVC controllers with views require, and AddRazorPages adds the services Razor Pages requires. Doporučujeme, aby aplikace dodržovaly tyto zásady vytváření názvů.We recommended that apps follow this naming convention. Microsoft.Extensions.DependencyInjectionDo oboru názvů umístěte metody rozšíření pro zapouzdření skupin registrací služby.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Životnost služebService lifetimes

Služby je možné zaregistrovat s jednou z následujících dob života:Services can be registered with one of the following lifetimes:

  • DočasnýTransient
  • OborScoped
  • SingletonSingleton

V následujících částech jsou popsány všechny předchozí životnosti.The following sections describe each of the preceding lifetimes. Vyberte odpovídající dobu života pro každou registrovanou službu.Choose an appropriate lifetime for each registered service.

DočasnýTransient

Dočasné služby životnosti se vytváří pokaždé, když jsou požadovány z kontejneru služby.Transient lifetime services are created each time they're requested from the service container. Tato životnost funguje nejlépe pro odlehčené bezstavové služby.This lifetime works best for lightweight, stateless services. Zaregistrujte přechodné služby pomocí AddTransient .Register transient services with AddTransient.

V aplikacích, které zpracovávají požadavky, jsou přechodné služby na konci žádosti zrušené.In apps that process requests, transient services are disposed at the end of the request.

OborScoped

Služby životnosti v oboru jsou vytvářeny jednou za požadavek klienta (připojení).Scoped lifetime services are created once per client request (connection). Zaregistrujte obor služeb pomocí AddScoped .Register scoped services with AddScoped.

V aplikacích, které zpracovávají požadavky, se vymezené služby na konci žádosti odstraní.In apps that process requests, scoped services are disposed at the end of the request.

Při použití Entity Framework Core AddDbContext metoda rozšíření registruje DbContext typy s vymezenou životností ve výchozím nastavení.When using Entity Framework Core, the AddDbContext extension method registers DbContext types with a scoped lifetime by default.

Neprovádějte Překlad oboru služby z typu singleton.Do not resolve a scoped service from a singleton. Může dojít k tomu, že služba má při zpracování dalších požadavků špatný stav.It may cause the service to have incorrect state when processing subsequent requests. Je to v pořádku:It's fine to:

  • Vyřešte službu typu Singleton z oboru nebo přechodné služby.Resolve a singleton service from a scoped or transient service.
  • Vyřešte oborovou službu z jiné vymezené nebo přechodné služby.Resolve a scoped service from another scoped or transient service.

Ve výchozím nastavení ve vývojovém prostředí vyřeší služba z jiné služby s delší životností výjimku.By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. Další informace najdete v tématu ověřování oboru.For more information, see Scope validation.

Pokud chcete používat vymezené služby v middlewaru, použijte jeden z následujících přístupů:To use scoped services in middleware, use one of the following approaches:

  • Službu zahájíte do služby nebo do metody middlewaru Invoke InvokeAsync .Inject the service into the middleware's Invoke or InvokeAsync method. Použití Injektáže konstruktoru vyvolá výjimku za běhu, protože vynutí, aby se služba s oborem chovala jako typ singleton.Using constructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. Ukázka v části Možnosti životního cyklu a registrace demonstruje InvokeAsync přístup.The sample in the Lifetime and registration options section demonstrates the InvokeAsync approach.
  • Použijte middleware založený na továrně.Use Factory-based middleware. Middleware registrované pomocí tohoto přístupu se aktivují na žádost klienta (připojení), která umožňuje vkládání oboru služeb do metody middlewaru InvokeAsync .Middleware registered using this approach is activated per client request (connection), which allows scoped services to be injected into the middleware's InvokeAsync method.

Další informace naleznete v tématu Zápis vlastního middlewaru ASP.NET Core.For more information, see Zápis vlastního middlewaru ASP.NET Core.

SingletonSingleton

Služba životnosti singleton je vytvořena buď:Singleton lifetime services are created either:

  • Při prvním vyžádání.The first time they're requested.
  • Vývojář při poskytování instance implementace přímo do kontejneru.By the developer, when providing an implementation instance directly to the container. Tento přístup je potřeba jenom zřídka.This approach is rarely needed.

Každý další požadavek používá stejnou instanci.Every subsequent request uses the same instance. Pokud aplikace vyžaduje chování singleton, umožněte kontejneru služby spravovat dobu života služby.If the app requires singleton behavior, allow the service container to manage the service's lifetime. Neimplementujte vzor návrhu singleton a poskytněte kód pro odstranění typu singleton.Don't implement the singleton design pattern and provide code to dispose of the singleton. Služby by nikdy neměly být odstraněny kódem, který službu vyřešil z kontejneru.Services should never be disposed by code that resolved the service from the container. Pokud je typ nebo objekt pro vytváření zaregistrován jako singleton, kontejner odstraní singleton automaticky.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

Registrovat služby s jedním prvkem pomocí AddSingleton .Register singleton services with AddSingleton. Služby s jedním prvkem musí být bezpečné pro přístup z více vláken a často se používají ve bezstavových službách.Singleton services must be thread safe and are often used in stateless services.

V aplikacích, které zpracovávají požadavky, jsou nejednoznačné služby uvolněny při ServiceProvider vyřazení aplikace z vypnutí.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed on application shutdown. Vzhledem k tomu, že paměť není uvolněna, dokud nebude aplikace vypnutá, zvažte použití paměti u služby s jedním prvkem.Because memory is not released until the app is shut down, consider memory use with a singleton service.

Upozornění

Neprovádějte Překlad oboru služby z typu singleton.Do not resolve a scoped service from a singleton. Může dojít k tomu, že služba má při zpracování dalších požadavků špatný stav.It may cause the service to have incorrect state when processing subsequent requests. Je dobré vyřešit službu typu Singleton z oboru nebo přechodné služby.It's fine to resolve a singleton service from a scoped or transient service.

Metody registrace službyService registration methods

Rozhraní poskytuje metody rozšíření pro registraci služby, které jsou užitečné v konkrétních scénářích:The framework provides service registration extension methods that are useful in specific scenarios:

MetodaMethod AutomatickyAutomatic
objectobject
odvoddisposal
NěkolikMultiple
implementaceimplementations
Pass – argumentyPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Příklad:Example:
services.AddSingleton<IMyDep, MyDep>();
AnoYes AnoYes NeNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
AnoYes AnoYes AnoYes
Add{LIFETIME}<{IMPLEMENTATION}>()
Příklad:Example:
services.AddSingleton<MyDep>();
AnoYes NeNo NeNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
NeNo AnoYes AnoYes
AddSingleton(new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
NeNo NeNo AnoYes

Další informace o vyřazení typů najdete v části věnované vyřazení služeb .For more information on type disposal, see the Disposal of services section. Při napodobování typů pro testováníje běžné použít více implementací.It's common to use multiple implementations when mocking types for testing.

Rozhraní poskytuje také TryAdd{LIFETIME} metody rozšíření, které registrují službu pouze v případě, že již není zaregistrována implementace.The framework also provides TryAdd{LIFETIME} extension methods, which register the service only if there isn't already an implementation registered.

V následujícím příkladu je volání AddSingleton registrováno MyDependency jako implementace pro IMyDependency .In the following example, the call to AddSingleton registers MyDependency as an implementation for IMyDependency. Volání TryAddSingleton nemá žádný účinek, protože IMyDependency již má registrovanou implementaci:The call to TryAddSingleton has no effect because IMyDependency already has a registered implementation:

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

Další informace naleznete v tématu:For more information, see:

Metody TryAddEnumerable (ServiceDescriptor) registrují službu pouze v případě, že již neexistují implementace stejného typu.The TryAddEnumerable(ServiceDescriptor) methods register the service only if there isn't already an implementation of the same type. Několik služeb je vyřešeno prostřednictvím IEnumerable<{SERVICE}> .Multiple services are resolved via IEnumerable<{SERVICE}>. Při registraci služeb by měl vývojář přidat instanci, pokud už jeden ze stejného typu není přidaný.When registering services, the developer should add an instance if one of the same type hasn't already been added. Obecně autoři knihovny používají TryAddEnumerable k tomu, aby nedocházelo k registraci více kopií implementace v kontejneru.Generally, library authors use TryAddEnumerable to avoid registering multiple copies of an implementation in the container.

V následujícím příkladu se první volání TryAddEnumerable registruje MyDependency jako implementace pro IMyDependency1 .In the following example, the first call to TryAddEnumerable registers MyDependency as an implementation for IMyDependency1. Druhý registr volání MyDependency pro IMyDependency2 .The second call registers MyDependency for IMyDependency2. Třetí volání nemá žádný účinek, protože IMyDependency1 už má registrovanou implementaci MyDependency :The third call has no effect because IMyDependency1 already has a registered implementation of MyDependency:

public interface IMyDependency1 { }
public interface IMyDependency2 { }

public class MyDependency : IMyDependency1, IMyDependency2 { }

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency2, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());

Registrace služby je obecně závislá na nezávislém pořadí s výjimkou registrace více implementací stejného typu.Service registration is generally order independent except when registering multiple implementations of the same type.

IServiceCollection je kolekce ServiceDescriptor objektů.IServiceCollection is a collection of ServiceDescriptor objects. Následující příklad ukazuje, jak zaregistrovat službu vytvořením a přidáním ServiceDescriptor :The following example shows how to register a service by creating and adding a ServiceDescriptor:

var myKey = Configuration["MyKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMyDependency),
    sp => new MyDependency5(myKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

Předdefinované Add{LIFETIME} metody používají stejný přístup.The built-in Add{LIFETIME} methods use the same approach. Podívejte se například na zdrojový kód AddScoped.For example, see the AddScoped source code.

Chování injektáže konstruktoruConstructor injection behavior

Služby je možné vyřešit pomocí:Services can be resolved by using:

Konstruktory mohou přijímat argumenty, které nejsou poskytovány vkládáním závislostí, ale argumenty musí přiřadit výchozí hodnoty.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Když jsou služby vyřešeny IServiceProvider nebo ActivatorUtilities , Injektáže konstruktoru vyžaduje veřejný konstruktor.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Když jsou služby vyřešeny nástrojem ActivatorUtilities , Injektáže konstruktoru vyžaduje, aby existoval pouze jeden příslušný konstruktor.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Přetížení konstruktoru jsou podporovaná, ale může existovat jenom jedno přetížení, jehož argumenty můžou být splněné vkládáním závislostí.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Entity Framework kontextyEntity Framework contexts

Ve výchozím nastavení se Entity Framework kontexty přidávají do kontejneru služby pomocí rozsahu životnosti , protože operace databáze webové aplikace jsou normálně vymezeny na žádost klienta.By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. Chcete-li použít jinou dobu života, určete dobu života pomocí AddDbContext přetížení.To use a different lifetime, specify the lifetime by using an AddDbContext overload. Služby pro danou dobu života by neměly používat kontext databáze s dobou životnosti, která je kratší než doba života služby.Services of a given lifetime shouldn't use a database context with a lifetime that's shorter than the service's lifetime.

Možnosti života a registraceLifetime and registration options

Chcete-li předvést rozdíl mezi životností služeb a jejich možnostmi registrace, zvažte následující rozhraní, která představují úlohu jako operaci s identifikátorem OperationId .To demonstrate the difference between service lifetimes and their registration options, consider the following interfaces that represent a task as an operation with an identifier, OperationId. V závislosti na tom, jak je doba platnosti služby operace nakonfigurovaná pro následující rozhraní, kontejner poskytuje buď stejné nebo jiné instance služby, když ho požaduje třída:Depending on how the lifetime of an operation's service is configured for the following interfaces, the container provides either the same or different instances of the service when requested by a class:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Následující Operation Třída implementuje všechna předchozí rozhraní.The following Operation class implements all of the preceding interfaces. OperationKonstruktor generuje identifikátor GUID a ukládá poslední 4 znaky ve OperationId vlastnosti:The Operation constructor generates a GUID and stores the last 4 characters in the OperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

Startup.ConfigureServicesMetoda vytvoří více registrací Operation třídy podle pojmenovaných životností:The Startup.ConfigureServices method creates multiple registrations of the Operation class according to the named lifetimes:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();

    services.AddRazorPages();
}

Ukázková aplikace ukazuje dobu života objektu v rámci i mezi požadavky.The sample app demonstrates object lifetimes both within and between requests. IndexModelA middleware vyžádá každý druh IOperation typu a protokoluje OperationId pro každý typ:The IndexModel and the middleware request each kind of IOperation type and log the OperationId for each:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

Podobně jako rozhraní IndexModel , middleware řeší stejné služby:Similar to the IndexModel, the middleware resolves the same services:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationTransient transientOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

Oborové služby musí být vyřešené v InvokeAsync metodě:Scoped services must be resolved in the InvokeAsync method:

public async Task InvokeAsync(HttpContext context,
    IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + _transientOperation.OperationId);
    _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

Výstup protokolovacího nástroje ukazuje:The logger output shows:

  • Přechodné objekty jsou vždy odlišné.Transient objects are always different. Přechodná OperationId hodnota se liší v IndexModel a v middlewaru.The transient OperationId value is different in the IndexModel and in the middleware.
  • Vymezené objekty jsou pro každý požadavek stejné, ale v každé žádosti se liší.Scoped objects are the same for each request but different across each request.
  • Objekty singleton jsou pro každý požadavek stejné.Singleton objects are the same for every request.

Chcete-li snížit výstup protokolování, nastavte v appsettings.Development.jsv souboru "Logging: LogLevel: Microsoft: Error":To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in the appsettings.Development.json file:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Volat služby z MainCall services from main

Vytvořte IServiceScope pomocí IServiceScopeFactory. CreateScope pro řešení oboru služby v rámci rozsahu aplikace.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Tento přístup je užitečný pro přístup k oboruované službě při spuštění za účelem spouštění úloh inicializace.This approach is useful to access a scoped service at startup to run initialization tasks.

Následující příklad ukazuje, jak přistupovat k oboru IMyDependency služby a volat jeho WriteMessage metodu v Program.Main :The following example shows how to access the scoped IMyDependency service and call its WriteMessage method in Program.Main:

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

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

            try
            {
                var myDependency = services.GetRequiredService<IMyDependency>();
                myDependency.WriteMessage("Call services from main");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Ověřování oboruScope validation

Když je aplikace spuštěná ve vývojovém prostředí a volá CreateDefaultBuilder , aby se vytvořila hostitel, použije výchozí poskytovatel služeb kontroly, aby ověřil, že:When the app runs in the Development environment and calls CreateDefaultBuilder to build the host, the default service provider performs checks to verify that:

  • Oborové služby se nevyřešily od poskytovatele kořenové služby.Scoped services aren't resolved from the root service provider.
  • Oborové služby se nevkládají do singleton.Scoped services aren't injected into singletons.

Poskytovatel kořenové služby se vytvoří, když BuildServiceProvider se zavolá.The root service provider is created when BuildServiceProvider is called. Doba života poskytovatele kořenové služby odpovídá době životnosti aplikace, když poskytovatel spustí aplikaci a je uvolněna při ukončení aplikace.The root service provider's lifetime corresponds to the app's lifetime when the provider starts with the app and is disposed when the app shuts down.

Oborové služby jsou uvolněny kontejnerem, který je vytvořil.Scoped services are disposed by the container that created them. Pokud je v kořenovém kontejneru vytvořena vymezená služba, doba života služby je efektivně povýšena na typ singleton, protože je uvolněna pouze kořenovým kontejnerem, když aplikace vypíná.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 the app shuts down. Při ověřování oborů služeb se tyto situace zachytí BuildServiceProvider .Validating service scopes catches these situations when BuildServiceProvider is called.

Další informace najdete v tématu ověřování oboru.For more information, see Scope validation.

Žádosti o službyRequest Services

Služby, které jsou k dispozici v rámci žádosti o ASP.NET Core, jsou zpřístupněny prostřednictvím kolekce HttpContext. RequestServices .The services available within an ASP.NET Core request are exposed through the HttpContext.RequestServices collection. Pokud jsou služby požadovány zevnitř žádosti, služby a jejich závislosti jsou řešeny z RequestServices kolekce.When services are requested from inside of a request, the services and their dependencies are resolved from the RequestServices collection.

Architektura vytvoří obor na žádost a RequestServices zpřístupní poskytovatele vymezeného rozsahu.The framework creates a scope per request and RequestServices exposes the scoped service provider. Všechny oborové služby platí, pokud je žádost aktivní.All scoped services are valid for as long as the request is active.

Poznámka

Preferovat požadavky na závislosti jako parametry konstruktoru pro překlad služeb z RequestServices kolekce.Prefer requesting dependencies as constructor parameters to resolving services from the RequestServices collection. Výsledkem jsou třídy, které jsou snáze testovány.This results in classes that are easier to test.

Navrhnout služby pro vkládání závislostíDesign services for dependency injection

Při navrhování služeb pro vkládání závislostí:When designing services for dependency injection:

  • Vyhněte se stavovým, statickým třídám a členům.Avoid stateful, static classes and members. Vyhněte se vytváření globálního stavu tím, že aplikace navrhujete, aby místo toho používaly služby singleton.Avoid creating global state by designing apps to use singleton services instead.
  • Vyhněte se přímému vytváření instancí závislých tříd v rámci služeb.Avoid direct instantiation of dependent classes within services. Přímá instance Couples kód na konkrétní implementaci.Direct instantiation couples the code to a particular implementation.
  • Vytvářejte služby s malým, dobře faktoringem a snadnou otestováním.Make services small, well-factored, and easily tested.

Pokud má třída mnoho vložených závislostí, může se jednat o znaménko, že třída má příliš mnoho zodpovědnosti a je v rozporu s principem jediné zodpovědnosti (SRP).If a class has a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates the Single Responsibility Principle (SRP). Pokuste se Refaktorovat třídu přesunutím některých jeho odpovědností do nových tříd.Attempt to refactor the class by moving some of its responsibilities into new classes. Mějte na paměti, že Razor stránky tříd modelu stránky a třídy KONTROLERU MVC by se měly zaměřit na uživatelské rozhraní.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns.

Vyřazení služebDisposal of services

Kontejner volá Dispose typy, které IDisposable vytvoří.The container calls Dispose for the IDisposable types it creates. Služby vyřešené z kontejneru by nikdy neměly být odstraněny vývojářem.Services resolved from the container should never be disposed by the developer. Pokud je typ nebo objekt pro vytváření zaregistrován jako singleton, kontejner odstraní singleton automaticky.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

V následujícím příkladu jsou služby vytvořeny pomocí kontejneru služby a automaticky odstraněny:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    
    var myKey = Configuration["MyKey"];
    services.AddSingleton<IService3>(sp => new Service3(myKey));

    services.AddRazorPages();
}
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

Konzola ladění po každé aktualizaci stránky indexu zobrazí následující výstup:The debug console shows the following output after each refresh of the Index page:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet
Service1.Dispose

Služby, které nevytvořil kontejner službyServices not created by the service container

Uvažujte následující kód:Consider the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new Service1());
    services.AddSingleton(new Service2());

    services.AddRazorPages();
}

V předchozím kódu:In the preceding code:

  • Instance služby nejsou vytvořeny kontejnerem služby.The service instances aren't created by the service container.
  • Rozhraní neodstraní služby automaticky.The framework doesn't dispose of the services automatically.
  • Vývojář zodpovídá za likvidaci služeb.The developer is responsible for disposing the services.

Doprovodné materiály k rozhraní IDisposable pro přechodné a sdílené instanceIDisposable guidance for Transient and shared instances

Přechodná, omezená doba životaTransient, limited lifetime

ScénářScenario

Aplikace vyžaduje IDisposable instanci s přechodným životností pro některý z následujících scénářů:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • Instance je vyřešena v kořenovém oboru (kořenovém kontejneru).The instance is resolved in the root scope (root container).
  • Instance by měla být uvolněna před ukončením oboru.The instance should be disposed before the scope ends.

ŘešeníSolution

Pomocí modelu továrny vytvořte instanci mimo nadřazený obor.Use the factory pattern to create an instance outside of the parent scope. V takové situaci by aplikace měla obvykle Create metodu, která volá přímo konstruktor finálního typu.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Pokud má konečný typ jiné závislosti, továrna může:If the final type has other dependencies, the factory can:

Sdílená instance, omezená doba životaShared instance, limited lifetime

ScénářScenario

Aplikace vyžaduje sdílenou IDisposable instanci napříč více službami, ale IDisposable instance by měla mít omezené trvání.The app requires a shared IDisposable instance across multiple services, but the IDisposable instance should have a limited lifetime.

ŘešeníSolution

Zaregistrujte instanci s vymezenou životností.Register the instance with a scoped lifetime. Použijte IServiceScopeFactory.CreateScope k vytvoření nového IServiceScope .Use IServiceScopeFactory.CreateScope to create a new IServiceScope. IServiceProviderPožadované služby získáte pomocí tohoto oboru.Use the scope's IServiceProvider to get required services. Pokud už tento obor nepotřebujete, vyřadit ho.Dispose the scope when it's no longer needed.

Obecné pokyny pro IDisposableGeneral IDisposable guidelines

  • Neregistrujte IDisposable instance s přechodným životností.Don't register IDisposable instances with a transient lifetime. Místo toho použijte výrobní model.Use the factory pattern instead.
  • IDisposableV kořenovém oboru neodstraňujte instance s přechodnou nebo vymezenou životností.Don't resolve IDisposable instances with a transient or scoped lifetime in the root scope. Jedinou výjimkou je, že pokud aplikace vytvoří nebo znovu vytvoří a odstraní IServiceProvider , ale to není ideální vzor.The only exception to this is if the app creates/recreates and disposes IServiceProvider, but this isn't an ideal pattern.
  • Příjem IDisposable závislosti přes di nevyžaduje, aby se příjemce sám implementoval IDisposable .Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. Příjemce IDisposable závislosti by neměl volat Dispose tuto závislost.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Použijte obory k řízení životnosti služeb.Use scopes to control the lifetimes of services. Obory nejsou hierarchické a mezi obory není žádné zvláštní připojení.Scopes aren't hierarchical, and there's no special connection among scopes.

Výchozí nahrazení kontejneru službyDefault service container replacement

Integrovaný kontejner služeb je navržený tak, aby sloužil potřebám architektury a většině spotřebitelských aplikací.The built-in service container is designed to serve the needs of the framework and most consumer apps. Pokud nepotřebujete konkrétní funkci, kterou nepodporuje, doporučujeme použít vestavěný kontejner, například:We recommend using the built-in container unless you need a specific feature that it doesn't support, such as:

  • Vkládání vlastnostíProperty injection
  • Vkládání na základě názvuInjection based on name
  • Podřízené kontejneryChild containers
  • Vlastní Správa životního cykluCustom lifetime management
  • Func<T> Podpora opožděné inicializaceFunc<T> support for lazy initialization
  • Registrace založená na konvencíchConvention-based registration

U ASP.NET Corech aplikací lze použít následující kontejnery třetích stran:The following third-party containers can be used with ASP.NET Core apps:

Bezpečnost vláknaThread safety

Vytvořte služby, které jsou v bezpečí pro přístup z více vláken.Create thread-safe singleton services. Pokud má služba typu Singleton závislost na přechodné službě, přechodná služba může také vyžadovat zabezpečení vlákna v závislosti na tom, jak je používá typ singleton.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending on how it's used by the singleton.

Výrobní metoda samostatné služby, jako je například druhý argument pro AddSingleton <TService> (IServiceCollection, Func <IServiceProvider,TService> ), nemusí být bezpečná pro přístup z více vláken.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. Podobně jako konstruktor typu ( static ), je zaručeno volání pouze jednou vláknem.Like a type (static) constructor, it's guaranteed to be called only once by a single thread.

DoporučeníRecommendations

  • async/await``Taskřešení služeb založené na základech se nepodporuje.async/await and Task based service resolution isn't supported. Vzhledem k tomu, že jazyk C# nepodporuje asynchronní konstruktory, použijte asynchronní metody po synchronním překladu služby.Because C# doesn't support asynchronous constructors, use asynchronous methods after synchronously resolving the service.

  • Neukládejte data a konfiguraci přímo do kontejneru služby.Avoid storing data and configuration directly in the service container. Například nákupní košík uživatele by neměl být obvykle přidán do kontejneru služby.For example, a user's shopping cart shouldn't typically be added to the service container. Konfigurace by měla používat vzor možností.Configuration should use the options pattern. Podobně nepoužívejte objekty "držitel dat", které existují pouze k povolení přístupu k jinému objektu.Similarly, avoid "data holder" objects that only exist to allow access to another object. Je lepší žádat o skutečnou položku přes DI.It's better to request the actual item via DI.

  • Vyhněte se statickému přístupu ke službám.Avoid static access to services. Vyhněte se například zachycení IApplicationBuilder. ApplicationServices jako statické pole nebo vlastnost pro použití jinde.For example, avoid capturing IApplicationBuilder.ApplicationServices as a static field or property for use elsewhere.

  • Snažte se DI Factory rychle a synchronně.Keep DI factories fast and synchronous.

  • Vyhněte se použití vzoru lokátoru služby.Avoid using the service locator pattern. Například Nevolejte GetService pro získání instance služby, když můžete místo toho použít di:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    Nesprávně:Incorrect:

    Nesprávný kód

    Správné:Correct:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Další změnou lokátoru služby, aby se zabránilo, je vložení továrny, která vyřeší závislosti za běhu.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Oba tyto postupy kombinují inverze strategií řízení .Both of these practices mix Inversion of Control strategies.

  • Vyhněte se statickému přístupu k HttpContext (například IHttpContextAccessor. HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

  • Vyhněte se volání do BuildServiceProvider v ConfigureServices .Avoid calls to BuildServiceProvider in ConfigureServices. BuildServiceProviderK volání obvykle dochází, když vývojář chce službu vyřešit ConfigureServices .Calling BuildServiceProvider typically happens when the developer wants to resolve a service in ConfigureServices. Zvažte například případ, kdy LoginPath je načten z konfigurace.For example, consider the case where the LoginPath is loaded from configuration. Vyhněte se následujícímu přístupu:Avoid the following approach:

    Chybný kód volající BuildServiceProvider

    Na předchozím obrázku vyberte zelenou vlnovku pod nadpisem services.BuildServiceProvider zobrazit následující upozornění ASP0000:In the preceding image, selecting the green wavy line under services.BuildServiceProvider shows the following ASP0000 warning:

    ASP0000 volání ' BuildServiceProvider ' z kódu aplikace vede k vytvoření další kopie ojedinělých služeb, které jsou vytvářeny.ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Zvažte alternativy, jako jsou třeba závislosti, jako jsou například služby, jako jsou parametry konfigurace.Consider alternatives such as dependency injecting services as parameters to 'Configure'.

    Volání BuildServiceProvider vytvoří druhý kontejner, který může vytvořit roztrhané jednoznačné objekty a způsobit odkazy na grafy objektů napříč více kontejnery.Calling BuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers.

    Správný způsob, jak získat LoginPath , je použít integrovanou podporu vzorce možností pro di:A correct way to get LoginPath is to use the options pattern's built-in support for DI:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
    
        services.AddOptions<CookieAuthenticationOptions>(
                            CookieAuthenticationDefaults.AuthenticationScheme)
            .Configure<IMyService>((options, myService) =>
            {
                options.LoginPath = myService.GetLoginPath();
            });
    
        services.AddRazorPages();
    }
    
  • Přechodné služby za použití jsou zachyceny kontejnerem k vyřazení.Disposable transient services are captured by the container for disposal. To může způsobit nevracení paměti, pokud je vyřešeno z kontejneru nejvyšší úrovně.This can turn into a memory leak if resolved from the top level container.

  • Povolte ověřování oboru, abyste se ujistili, že aplikace nemá k dispozici jednoznačné služby, které zachytí obor služeb.Enable scope validation to make sure the app doesn't have singletons that capture scoped services. Další informace najdete v tématu ověřování oboru.For more information, see Scope validation.

Podobně jako u všech sad doporučení se může stát, že se vyžaduje ignorování doporučení.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Výjimky jsou zřídka, většinou zvláštní případy v rámci samotného rozhraní.Exceptions are rare, mostly special cases within the framework itself.

DI je alternativou ke vzorům statických nebo globálních přístupů k objektům.DI is an alternative to static/global object access patterns. Je možné, že nebudete moci využít výhody DI, pokud je kombinujete se statickým přístupem k objektům.You may not be able to realize the benefits of DI if you mix it with static object access.

Sadu jader je aplikační architektura pro vytváření modulárních aplikací s více klienty na ASP.NET Core.Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. Další informace najdete v dokumentaci k sadě sad.For more information, see the Orchard Core Documentation.

V ukázkách Core Core najdete příklady vytváření modulárních a víceklientské aplikací s využitím jenom sady Core Core Framework bez jakýchkoli funkcí specifických pro CMS.See the Orchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Služby poskytované rozhranímFramework-provided services

Startup.ConfigureServicesMetoda zaregistruje služby, které aplikace používá, včetně funkcí platformy, jako je například Entity Framework Core a ASP.NET Core MVC.The Startup.ConfigureServices method registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Zpočátku IServiceCollection ConfigureServices služba má služby definované rozhraním v závislosti na tom, jak byl hostitel nakonfigurovaný.Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. Pro aplikace založené na šablonách ASP.NET Core registruje rozhraní více než 250 služeb.For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

V následující tabulce je uveden malý vzorek těchto služeb registrovaných v rozhraní:The following table lists a small sample of these framework-registered services:

Typ službyService Type Doba platnostiLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory DočasnýTransient
IHostApplicationLifetime SingletonSingleton
IWebHostEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter DočasnýTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory DočasnýTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> DočasnýTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Další zdroje informacíAdditional resources

Steve Smith, Scott Addiea Brandon DahlerBy Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core podporuje vzor návrhu softwaru pro vkládání závislostí (DI), což je technika pro dosažení inverze ovládacího prvku (IOC) mezi třídami a jejich závislostmi.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.

Další informace, které jsou specifické pro vkládání závislostí v rámci řadičů MVC, najdete v tématu Vkládání závislostí do řadičů v ASP.NET Core .For more information specific to dependency injection within MVC controllers, see Vkládání závislostí do řadičů v ASP.NET Core.

Zobrazit nebo stáhnout ukázkový kód (Jak stáhnout)View or download sample code (how to download)

Přehled injektáže závislostiOverview of dependency injection

Závislost je libovolný objekt, který vyžaduje jiný objekt.A dependency is any object that another object requires. Projděte si následující MyDependency třídu s WriteMessage metodou, na které jsou závislé jiné třídy v aplikaci: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);
    }
}

Instance MyDependency třídy může být vytvořena, aby byla WriteMessage Metoda k dispozici pro třídu.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. MyDependencyTřída je závislostí IndexModel třídy: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.");
    }
}

Třída vytvoří a přímo závisí na MyDependency instanci.The class creates and directly depends on the MyDependency instance. Závislosti kódu (například předchozí příklad) jsou problematické a je třeba se jim vyhnout z následujících důvodů:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Chcete-li nahradit MyDependency jinou implementací, třída musí být upravena.To replace MyDependency with a different implementation, the class must be modified.
  • Pokud MyDependency má závislosti, musí být nakonfigurovány třídou.If MyDependency has dependencies, they must be configured by the class. Ve velkém projektu s více třídami, v závislosti na tom MyDependency , je kód konfigurace rozptýlen napříč aplikací.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Tato implementace je obtížná pro testování částí.This implementation is difficult to unit test. Aplikace by měla používat třídu typu "nebo zástupné procedury" MyDependency , což není u tohoto přístupu možné.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

Vkládání závislostí řeší tyto problémy prostřednictvím:Dependency injection addresses these problems through:

  • Použití rozhraní nebo základní třídy k abstrakci implementace závislosti.The use of an interface or base class to abstract the dependency implementation.
  • Registrace závislosti v kontejneru služby.Registration of the dependency in a service container. ASP.NET Core poskytuje integrovaný kontejner služeb IServiceProvider .ASP.NET Core provides a built-in service container, IServiceProvider. Služby jsou registrovány v metodě aplikace Startup.ConfigureServices .Services are registered in the app's Startup.ConfigureServices method.
  • Vložení služby do konstruktoru třídy, kde se používá.Injection of the service into the constructor of the class where it's used. Rozhraní přebírá zodpovědnost za vytvoření instance závislosti a její odstranění, když už ji nepotřebujete.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

V ukázkové aplikaci IMyDependency definuje rozhraní metodu, kterou služba poskytuje aplikaci:In the sample app, the IMyDependency interface defines a method that the service provides to the app:

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

Toto rozhraní je implementováno konkrétním typem 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);
    }
}

MyDependency vyžádá ILogger<TCategoryName> v jeho konstruktoru.MyDependency requests an ILogger<TCategoryName> in its constructor. Není neobvyklé používat vkládání závislostí v zřetězeném způsobem.It's not unusual to use dependency injection in a chained fashion. Každá požadovaná závislost zase vyžádá své vlastní závislosti.Each requested dependency in turn requests its own dependencies. Kontejner vyřeší závislosti v grafu a vrátí plně přeloženou službu.The container resolves the dependencies in the graph and returns the fully resolved service. Kolektivní sada závislostí, které je třeba vyřešit, se obvykle označuje jako strom závislosti, graf závislostinebo graf objektů.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency a ILogger<TCategoryName> musí být registrována v kontejneru služby.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency je zaregistrován v Startup.ConfigureServices .IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> je zaregistrované v infrastruktuře abstrakce protokolování, takže se jedná o službu , která je zaregistrovaná ve výchozím rozhraní.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

Kontejner se překládá ILogger<TCategoryName> tím, že využije (Obecné) otevřené typya eliminuje nutnost registrace každého (obecného) konstruovaného 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<>), typeof(Logger<>));

V ukázkové aplikaci IMyDependency je služba zaregistrovaná u konkrétního typu MyDependency .In the sample app, the IMyDependency service is registered with the concrete type MyDependency. Registruje rozsah platnosti služby po dobu životnosti jediného požadavku.The registration scopes the service lifetime to the lifetime of a single request. Doby platnosti služeb jsou popsány dále v tomto tématu.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>();
}

Poznámka

Jednotlivé services.Add{SERVICE_NAME} metody rozšíření přidávají (a případně nakonfigurují) služby.Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Například services.AddMvc() přidá Razor stránky služeb a MVC vyžaduje.For example, services.AddMvc() adds the services Razor Pages and MVC require. Doporučujeme, aby aplikace dodržovaly tuto konvenci.We recommended that apps follow this convention. Do oboru názvů Microsoft. Extensions. DependencyInjection umístěte metody rozšíření pro zapouzdření skupin registrací služby.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Pokud konstruktor služby vyžaduje vestavěný typ, například string ,, může být typ vložen pomocí Konfigurace nebo vzoru možností: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
    }

    ...
}

Instance služby je požadována prostřednictvím konstruktoru třídy, kde je služba používána a přiřazena k soukromému poli.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. Pole se používá pro přístup ke službě podle potřeby v celé třídě.The field is used to access the service as necessary throughout the class.

V ukázkové aplikaci IMyDependency je instance vyžádána a používána pro volání WriteMessage metody služby: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.");
    }
}

Služby vložené do spuštěníServices injected into Startup

StartupPři použití obecného hostitele () mohou být do konstruktoru vloženy pouze následující typy služeb IHostBuilder :Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

Služby je možné vložit do Startup.Configure :Services can be injected into Startup.Configure:

public void Configure(IApplicationBuilder app, IOptions<MyOptions> options)
{
    ...
}

Další informace naleznete v tématu Spuštění aplikace v ASP.NET Core.For more information, see Spuštění aplikace v ASP.NET Core.

Služby poskytované rozhranímFramework-provided services

Startup.ConfigureServicesMetoda zodpovídá za definování služeb, které aplikace používá, včetně funkcí platformy, jako je například Entity Framework Core a ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Zpočátku IServiceCollection ConfigureServices služba má služby definované rozhraním v závislosti na tom, jak byl hostitel nakonfigurovaný.Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. Pro aplikaci, která je založená na šabloně ASP.NET Core, není běžné, že mají stovky služeb zaregistrovaných v rámci rozhraní.It's not uncommon for an app based on an ASP.NET Core template to have hundreds of services registered by the framework. V následující tabulce je uvedena malá ukázka služeb registrovaných v rozhraní.A small sample of framework-registered services is listed in the following table.

Typ službyService Type Doba platnostiLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory DočasnýTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter DočasnýTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory DočasnýTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> DočasnýTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Registrace dalších služeb pomocí rozšiřujících metodRegister additional services with extension methods

Pokud je k dispozici metoda rozšíření kolekce služeb pro registraci služby (a jejích závislých služeb, v případě potřeby), konvence používá jedinou Add{SERVICE_NAME} metodu rozšíření k registraci všech služeb vyžadovaných touto službou.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. Následující kód je příkladem přidání dalších služeb do kontejneru pomocí metod rozšíření AddDbContext <TContext> a AddIdentityCore :The following code is an example of how to add additional services to the container using the extension methods AddDbContext<TContext> and AddIdentityCore:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

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

    ...
}

Další informace najdete ServiceCollection ve třídě v dokumentaci k rozhraní API.For more information, see the ServiceCollection class in the API documentation.

Životnost služebService lifetimes

Vyberte odpovídající dobu života pro každou registrovanou službu.Choose an appropriate lifetime for each registered service. Služby ASP.NET Core Services je možné nakonfigurovat s následujícími životnostmi:ASP.NET Core services can be configured with the following lifetimes:

DočasnýTransient

Služba přechodných životností ( AddTransient ) se vytvoří pokaždé, když se požadují z kontejneru služby.Transient lifetime services (AddTransient) are created each time they're requested from the service container. Tato životnost funguje nejlépe pro odlehčené bezstavové služby.This lifetime works best for lightweight, stateless services.

V aplikacích, které zpracovávají požadavky, jsou přechodné služby na konci žádosti zrušené.In apps that process requests, transient services are disposed at the end of the request.

OborScoped

Oborové služby () s rozsahem platnosti ( AddScoped ) se vytvoří jednou za požadavek klienta (připojení).Scoped lifetime services (AddScoped) are created once per client request (connection).

V aplikacích, které zpracovávají požadavky, se vymezené služby na konci žádosti odstraní.In apps that process requests, scoped services are disposed at the end of the request.

Upozornění

Při použití oboru služby v middlewaru založit službu do Invoke InvokeAsync metody nebo.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Nepoužívejte vkládání pomocí injektáže konstruktoru , protože vynutí, aby se služba chovala jako typ singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Další informace naleznete v tématu Zápis vlastního middlewaru ASP.NET Core.For more information, see Zápis vlastního middlewaru ASP.NET Core.

SingletonSingleton

Služba singleton životnosti ( AddSingleton ) je vytvořena při prvním vyžádání (nebo při Startup.ConfigureServices spuštění a instance je určena pro registraci služby).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ždý další požadavek používá stejnou instanci.Every subsequent request uses the same instance. Pokud aplikace vyžaduje chování singleton, doporučuje se povolit kontejneru služby spravovat dobu života služby.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Neimplementujte vzor návrhu singleton a poskytněte uživatelský kód pro správu životního cyklu objektu ve třídě.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

V aplikacích, které zpracovávají požadavky, jsou nejednoznačné služby uvolněny, když ServiceProvider je uvolněna při vypnutí aplikace.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed at app shutdown.

Upozornění

Není bezpečné přeložit oborovou službu z typu singleton.It's dangerous to resolve a scoped service from a singleton. Může dojít k tomu, že služba má při zpracování dalších požadavků špatný stav.It may cause the service to have incorrect state when processing subsequent requests.

Metody registrace službyService registration methods

Metody rozšíření registrace služby nabízejí přetížení, která jsou užitečná pro konkrétní scénáře.Service registration extension methods offer overloads that are useful in specific scenarios.

MetodaMethod AutomatickyAutomatic
objectobject
odvoddisposal
NěkolikMultiple
implementaceimplementations
Pass – argumentyPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Příklad:Example:
services.AddSingleton<IMyDep, MyDep>();
AnoYes AnoYes NeNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep("A string!"));
AnoYes AnoYes AnoYes
Add{LIFETIME}<{IMPLEMENTATION}>()
Příklad:Example:
services.AddSingleton<MyDep>();
AnoYes NeNo NeNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep("A string!"));
NeNo AnoYes AnoYes
AddSingleton(new {IMPLEMENTATION})
Příklady:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep("A string!"));
NeNo NeNo AnoYes

Další informace o vyřazení typů najdete v části věnované vyřazení služeb .For more information on type disposal, see the Disposal of services section. Běžným scénářem pro více implementací je vytvoření typů pro testování.A common scenario for multiple implementations is mocking types for testing.

TryAdd{LIFETIME} metody registrují službu jenom v případě, že už není zaregistrovaná implementace.TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

V následujícím příkladu se první řádek registruje MyDependency pro IMyDependency .In the following example, the first line registers MyDependency for IMyDependency. Druhý řádek nemá žádný účinek, protože IMyDependency už má registrovanou implementaci: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>();

Další informace naleznete v tématu:For more information, see:

Metody TryAddEnumerable (ServiceDescriptor) registrují službu pouze v případě, že již není implementace stejného typu.TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. Několik služeb je vyřešeno prostřednictvím IEnumerable<{SERVICE}> .Multiple services are resolved via IEnumerable<{SERVICE}>. Při registraci služeb chce vývojář přidat instanci jenom v případě, že už jeden ze stejného typu není přidaný.When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. Obecně je tato metoda používána autory knihovny k tomu, aby nedocházelo k registraci dvou kopií instance v kontejneru.Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

V následujícím příkladu se první řádek registruje MyDep pro IMyDep1 .In the following example, the first line registers MyDep for IMyDep1. Druhý řádek se registruje MyDep pro IMyDep2 .The second line registers MyDep for IMyDep2. Třetí řádek nemá žádný vliv, protože IMyDep1 už má registrovanou implementaci MyDep :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>());

Chování injektáže konstruktoruConstructor injection behavior

Služby je možné vyřešit pomocí dvou mechanismů:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities: Povoluje vytvoření objektu bez registrace služby v kontejneru vkládání závislostí.ActivatorUtilities: Permits object creation without service registration in the dependency injection container. ActivatorUtilities se používá s uživateli s přístupem k uživatelům, jako jsou například rutiny značek, řadiče MVC a pořadače modelů.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Konstruktory mohou přijímat argumenty, které nejsou poskytovány vkládáním závislostí, ale argumenty musí přiřadit výchozí hodnoty.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Když jsou služby vyřešeny IServiceProvider nebo ActivatorUtilities , Injektáže konstruktoru vyžaduje veřejný konstruktor.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Když jsou služby vyřešeny nástrojem ActivatorUtilities , Injektáže konstruktoru vyžaduje, aby existoval pouze jeden příslušný konstruktor.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Přetížení konstruktoru jsou podporovaná, ale může existovat jenom jedno přetížení, jehož argumenty můžou být splněné vkládáním závislostí.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Entity Framework kontextyEntity Framework contexts

Entity Framework kontexty se obvykle přidávají do kontejneru služby pomocí rozsahu životnosti , protože operace databáze webové aplikace jsou obvykle vymezeny na požadavky 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. Výchozí doba života je vymezena v případě, že při registraci kontextu databáze není zadána doba života při AddDbContext <TContext> přetížení.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. Služby pro danou dobu života by neměly používat kontext databáze s kratší dobou životnosti než služba.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Možnosti života a registraceLifetime and registration options

Chcete-li předvést rozdíl mezi možnostmi životního cyklu a registrací, zvažte následující rozhraní, která představují úlohy jako operaci s jedinečným identifikátorem OperationId .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. V závislosti na tom, jak je životnost provozní služby nakonfigurována pro následující rozhraní, kontejner poskytuje buď stejnou nebo jinou instanci služby, když je vyžádána třídou: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
{
}

Rozhraní jsou implementována ve Operation třídě.The interfaces are implemented in the Operation class. OperationKonstruktor generuje identifikátor GUID, pokud není dodán: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; }
}

OperationServiceJe zaregistrován, který závisí na každém z ostatních Operation typů.An OperationService is registered that depends on each of the other Operation types. Když OperationService je požadavek proveden prostřednictvím injektáže závislosti, obdrží buď novou instanci každé služby, nebo existující instanci na základě životnosti závislé služby.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.

  • Když se v případě vyžádání z kontejneru vytvoří přechodné služby, OperationId liší se od IOperationTransient služby OperationId 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 přijme novou instanci IOperationTransient třídy.OperationService receives a new instance of the IOperationTransient class. Nová instance vrací jinou instanci OperationId .The new instance yields a different OperationId.
  • Když se vytvoří vymezené služby pro jednotlivé požadavky klienta, OperationId IOperationScoped služba je stejná jako OperationService v rámci žádosti 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. V rámci požadavků klientů obě služby sdílejí jinou OperationId hodnotu.Across client requests, both services share a different OperationId value.
  • Pokud se služby instance singleton a singleton vytvoří jednou a použije se pro všechny požadavky klientů a všechny služby, OperationId je v rámci všech žádostí o službu konstantní.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; }
}

V Startup.ConfigureServices je každý typ přidán do kontejneru podle jeho pojmenované životnosti: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>();
}

IOperationSingletonInstanceSlužba používá konkrétní instanci se známým ID Guid.Empty .The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. Je jasné, že pokud se tento typ používá (jeho identifikátor GUID je všechny nuly).It's clear when this type is in use (its GUID is all zeroes).

Ukázková aplikace ukazuje dobu života objektu v rámci a mezi jednotlivými požadavky.The sample app demonstrates object lifetimes within and between individual requests. Ukázková aplikace IndexModel požaduje každý druh IOperation typu a OperationService .The sample app's IndexModel requests each kind of IOperation type and the OperationService. Stránka potom zobrazí všechny hodnoty třídy modelu stránky a OperationId hodnoty služby prostřednictvím přiřazení vlastností: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.");
    }
}

Výsledkem dvou následujících výstupů jsou výsledky dvou požadavků:Two following output shows the results of two requests:

První požadavek:First request:

Operace kontroleru:Controller operations:

Přechodný: d233e165-f417-469b-A866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Vymezené: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService OperationsOperationService operations:

Přechodný: c6b049eb-1318-4E31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Vymezené: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Druhá žádost:Second request:

Operace kontroleru:Controller operations:

Přechodný: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Vymezené: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService OperationsOperationService operations:

Přechodný: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Vymezené: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Pozor, které OperationId hodnoty se liší v rámci požadavku a mezi požadavky:Observe which of the OperationId values vary within a request and between requests:

  • Přechodné objekty jsou vždy odlišné.Transient objects are always different. Přechodná OperationId hodnota pro první i druhý požadavek klienta se liší pro obě OperationService operace i mezi požadavky klientů.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Každé žádosti o službu a žádosti klienta se poskytne nová instance.A new instance is provided to each service request and client request.
  • Vymezené objekty jsou v požadavku klienta stejné, ale v rámci požadavků klientů se liší.Scoped objects are the same within a client request but different across client requests.
  • Objekty singleton jsou stejné pro každý objekt a každý požadavek bez ohledu na to, zda Operation je instance uvedena v Startup.ConfigureServices .Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

Volat služby z MainCall services from main

Vytvořte IServiceScope pomocí IServiceScopeFactory. CreateScope pro řešení oboru služby v rámci rozsahu aplikace.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Tento přístup je užitečný pro přístup k oboruované službě při spuštění za účelem spouštění úloh inicializace.This approach is useful to access a scoped service at startup to run initialization tasks. Následující příklad ukazuje, jak získat kontext pro MyScopedService Program.Main :The following example shows how to obtain a context for the MyScopedService in Program.Main:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

public class Program
{
    public static async Task 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.");
            }
        }

        await host.RunAsync();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Ověřování oboruScope validation

Když je aplikace spuštěná ve vývojovém prostředí, provede výchozí poskytovatel služeb kontroly, aby ověřil, že:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Oborové služby nejsou přímo nebo nepřímo vyřešeny od poskytovatele kořenové služby.Scoped services aren't directly or indirectly resolved from the root service provider.
  • Oborové služby nejsou přímo nebo nepřímo vložené do singleton.Scoped services aren't directly or indirectly injected into singletons.

Poskytovatel kořenové služby se vytvoří, když BuildServiceProvider se zavolá.The root service provider is created when BuildServiceProvider is called. Doba života poskytovatele kořenové služby odpovídá době životnosti aplikace nebo serveru, když se poskytovatel spustí s aplikací a je uvolněný při ukončení aplikace.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.

Oborové služby jsou uvolněny kontejnerem, který je vytvořil.Scoped services are disposed by the container that created them. Pokud je v kořenovém kontejneru vytvořena vymezená služba, doba života služby je efektivně povýšena na typ singleton, protože je uvolněna pouze kořenovým kontejnerem při vypnutí aplikace nebo serveru.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. Při ověřování oborů služeb se tyto situace zachytí BuildServiceProvider .Validating service scopes catches these situations when BuildServiceProvider is called.

Další informace naleznete v tématu ASP.NET Core webového hostitele.For more information, see ASP.NET Core webového hostitele.

Žádosti o službyRequest Services

Služby, které jsou k dispozici v rámci žádosti o ASP.NET Core, HttpContext jsou zpřístupněny prostřednictvím kolekce HttpContext. RequestServices .The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Žádosti o služby reprezentují služby nakonfigurované a požadované v rámci aplikace.Request Services represent the services configured and requested as part of the app. Když objekty určují závislosti, jsou splněny typy nalezenými v RequestServices , ne ApplicationServices .When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Obecně platí, že aplikace by tyto vlastnosti neměla používat přímo.Generally, the app shouldn't use these properties directly. Místo toho požádejte o typy, které třídy vyžadují prostřednictvím konstruktorů třídy, a umožněte rozhraní vložit závislosti.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. To poskytuje třídy, které jsou snáze testovány.This yields classes that are easier to test.

Poznámka

Preferovat požadavky na závislosti jako parametry konstruktoru pro přístup ke RequestServices kolekci.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Navrhnout služby pro vkládání závislostíDesign services for dependency injection

Osvědčené postupy:Best practices are to:

  • Navrhněte služby pro použití injektáže závislosti k získání jejich závislostí.Design services to use dependency injection to obtain their dependencies.
  • Vyhněte se stavovým, statickým třídám a členům.Avoid stateful, static classes and members. Navrhněte aplikace, aby místo toho používaly služby s jedním prvkem, což se vyhne vytváření globálního stavu.Design apps to use singleton services instead, which avoid creating global state.
  • Vyhněte se přímému vytváření instancí závislých tříd v rámci služeb.Avoid direct instantiation of dependent classes within services. Přímá instance Couples kód na konkrétní implementaci.Direct instantiation couples the code to a particular implementation.
  • Vytvářejte třídy aplikací malými, dobře faktoringně a snadno testované.Make app classes small, well-factored, and easily tested.

Pokud se zdá, že třída má příliš mnoho vložených závislostí, obvykle se jedná o znaménko, že třída má příliš mnoho zodpovědnosti a je v rozporu s principem jediné zodpovědnosti (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). Pokuste se Refaktorovat třídu přesunutím některých odpovědností do nové třídy.Attempt to refactor the class by moving some of its responsibilities into a new class. Mějte na paměti, že Razor stránky tříd modelu stránky a třídy KONTROLERU MVC by se měly zaměřit na uživatelské rozhraní.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Obchodní pravidla a podrobnosti implementace přístupu k datům by měly být uchovávány ve třídách, které jsou vhodné pro tyto samostatné obavy.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Vyřazení služebDisposal of services

Kontejner volá Dispose typy, které IDisposable vytvoří.The container calls Dispose for the IDisposable types it creates. Pokud je instance přidána do kontejneru pomocí uživatelského kódu, nebude uvolněna automaticky.If an instance is added to the container by user code, it isn't disposed automatically.

V následujícím příkladu jsou služby vytvořeny pomocí kontejneru služby a automaticky odstraněny:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

public interface IService3 {}
public class Service3 : IService3, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<IService3>(sp => new Service3());
}

V následujícím příkladu:In the following example:

  • Instance služby nejsou vytvořeny kontejnerem služby.The service instances aren't created by the service container.
  • Zamýšlené životnosti služeb nejsou rozhraním známy.The intended service lifetimes aren't known by the framework.
  • Rozhraní neodstraní služby automaticky.The framework doesn't dispose of the services automatically.
  • Pokud se služby explicitně neodstraní v kódu pro vývojáře, zůstanou zachovány, dokud se aplikace neukončí.If the services aren't explicitly disposed in developer code, they persist until the app shuts down.
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<Service1>(new Service1());
    services.AddSingleton(new Service2());
}

Doprovodné materiály k rozhraní IDisposable pro přechodné a sdílené instanceIDisposable guidance for Transient and shared instances

Přechodná, omezená doba životaTransient, limited lifetime

ScénářScenario

Aplikace vyžaduje IDisposable instanci s přechodným životností pro některý z následujících scénářů:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • Instance je vyřešena v kořenovém oboru.The instance is resolved in the root scope.
  • Instance by měla být uvolněna před ukončením oboru.The instance should be disposed before the scope ends.

ŘešeníSolution

Pomocí modelu továrny vytvořte instanci mimo nadřazený obor.Use the factory pattern to create an instance outside of the parent scope. V takové situaci by aplikace měla obvykle Create metodu, která volá přímo konstruktor finálního typu.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Pokud má konečný typ jiné závislosti, továrna může:If the final type has other dependencies, the factory can:

Sdílená instance, omezená doba životaShared Instance, limited lifetime

ScénářScenario

Aplikace vyžaduje sdílenou IDisposable instanci napříč více službami, ale IDisposable měla by mít omezené trvání.The app requires a shared IDisposable instance across multiple services, but the IDisposable should have a limited lifetime.

ŘešeníSolution

Zaregistrujte instanci s vymezenou životností.Register the instance with a Scoped lifetime. Použijte IServiceScopeFactory.CreateScope k zahájení a vytvoření nového IServiceScope .Use IServiceScopeFactory.CreateScope to start and create a new IServiceScope. IServiceProviderPožadované služby získáte pomocí tohoto oboru.Use the scope's IServiceProvider to get required services. Vyřadit rozsah, pokud by životnost měla končit.Dispose the scope when the lifetime should end.

Obecné pokynyGeneral Guidelines

  • Neregistrujte IDisposable instance s přechodným oborem.Don't register IDisposable instances with a Transient scope. Místo toho použijte výrobní model.Use the factory pattern instead.
  • V kořenovém oboru neřešte přechodné nebo vymezené IDisposable instance.Don't resolve Transient or Scoped IDisposable instances in the root scope. Jedinou obecnou výjimkou je, když aplikace vytvoří nebo znovu vytvoří a odstraní IServiceProvider , což není ideální vzor.The only general exception is when the app creates/recreates and disposes the IServiceProvider, which isn't an ideal pattern.
  • Příjem IDisposable závislosti přes di nevyžaduje, aby se příjemce sám implementoval IDisposable .Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. Příjemce IDisposable závislosti by neměl volat Dispose tuto závislost.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Obory by měly sloužit k řízení životnosti služeb.Scopes should be used to control lifetimes of services. Obory nejsou hierarchické a mezi obory není žádné zvláštní připojení.Scopes aren't hierarchical, and there's no special connection among scopes.

Výchozí nahrazení kontejneru službyDefault service container replacement

Integrovaný kontejner služeb je navržený tak, aby sloužil potřebám architektury a většině spotřebitelských aplikací.The built-in service container is designed to serve the needs of the framework and most consumer apps. Pokud nepotřebujete určitou funkci, kterou integrovaný kontejner nepodporuje, doporučujeme použít vestavěný kontejner, například:We recommend using the built-in container unless you need a specific feature that the built-in container doesn't support, such as:

  • Vkládání vlastnostíProperty injection
  • Vkládání na základě názvuInjection based on name
  • Podřízené kontejneryChild containers
  • Vlastní Správa životního cykluCustom lifetime management
  • Func<T> Podpora opožděné inicializaceFunc<T> support for lazy initialization
  • Registrace založená na konvencíchConvention-based registration

U ASP.NET Corech aplikací lze použít následující kontejnery třetích stran:The following third-party containers can be used with ASP.NET Core apps:

Bezpečnost vláknaThread safety

Vytvořte služby, které jsou v bezpečí pro přístup z více vláken.Create thread-safe singleton services. Pokud má služba typu Singleton závislost na přechodné službě, přechodná služba může také vyžadovat zabezpečení vlákna v závislosti na tom, jak je používá typ singleton.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.

Výrobní metoda samostatné služby, jako je například druhý argument pro AddSingleton <TService> (IServiceCollection, Func <IServiceProvider,TService> ), nemusí být bezpečná pro přístup z více vláken.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. Podobně jako konstruktor typu ( static ), je zaručeno, že bude volán jednou vláknem.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

DoporučeníRecommendations

  • async/await``Taskřešení služeb a se nepodporuje.async/await and Task based service resolution is not supported. Jazyk C# nepodporuje asynchronní konstruktory; Proto je doporučeným vzorem použití asynchronních metod po synchronním překladu služby.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Neukládejte data a konfiguraci přímo do kontejneru služby.Avoid storing data and configuration directly in the service container. Například nákupní košík uživatele by neměl být obvykle přidán do kontejneru služby.For example, a user's shopping cart shouldn't typically be added to the service container. Konfigurace by měla používat vzor možností.Configuration should use the options pattern. Podobně nepoužívejte objekty "držitel dat", které existují pouze k povolení přístupu k jinému objektu.Similarly, avoid "data holder" objects that only exist to allow access to some other object. Je lepší žádat o skutečnou položku přes DI.It's better to request the actual item via DI.

  • Vyhněte se statickému přístupu ke službám.Avoid static access to services. Vyhněte se například statickému psaní IApplicationBuilder. ApplicationServices pro použití jinde.For example, avoid statically-typing IApplicationBuilder.ApplicationServices for use elsewhere.

  • Vyhněte se použití vzoru lokátoru služby, který kombinuje inverzi strategií řízení .Avoid using the service locator pattern, which mixes Inversion of Control strategies.

    • Nevolejte GetService pro získání instance služby, když můžete místo toho použít di:Don't invoke GetService to obtain a service instance when you can use DI instead:

      Nesprávně:Incorrect:

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

      Správné:Correct:

      public class MyClass
      {
          private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
      
          public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
          {
              _optionsMonitor = optionsMonitor;
          }
      
          public void MyMethod()
          {
              var option = _optionsMonitor.CurrentValue.Option;
      
              ...
          }
      }
      
  • Vyhněte se vkládání továrny, která řeší závislosti za běhu pomocí GetService .Avoid injecting a factory that resolves dependencies at runtime using GetService.

  • Vyhněte se statickému přístupu k HttpContext (například IHttpContextAccessor. HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Podobně jako u všech sad doporučení se může stát, že se vyžaduje ignorování doporučení.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Výjimky jsou zřídka, většinou zvláštní případy v rámci samotného rozhraní.Exceptions are rare, mostly special cases within the framework itself.

DI je alternativou ke vzorům statických nebo globálních přístupů k objektům.DI is an alternative to static/global object access patterns. Je možné, že nebudete moci využít výhody DI, pokud je kombinujete se statickým přístupem k objektům.You may not be able to realize the benefits of DI if you mix it with static object access.

Další zdroje informacíAdditional resources