Injeção de dependência no ASP.NET CoreDependency injection in ASP.NET Core

Por Steve Smith, Scott Addie e Luke LathamBy Steve Smith, Scott Addie, and Luke Latham

O ASP.NET Core dá suporte ao padrão de design de software de DI (injeção de dependência), que é uma técnica para conseguir IoC (inversão de controle) entre classes e suas dependências.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.

Para obter mais informações específicas sobre injeção de dependência em controladores de MVC, consulte Injeção de dependência em controladores no ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Injeção de dependência em controladores no ASP.NET Core.

Exibir ou baixar código de exemplo (como baixar)View or download sample code (how to download)

Visão geral da injeção de dependênciaOverview of dependency injection

Uma dependência é qualquer objeto exigido por outro objeto.A dependency is any object that another object requires. Examine a classe MyDependency a seguir com um método WriteMessage do qual outras classes em um aplicativo dependem: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);
    }
}

Uma instância da classe MyDependency pode ser criada para tornar o método WriteMessage disponível para uma classe.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. A classe MyDependency é uma dependência da classe IndexModel:The MyDependency class is a dependency of the IndexModel class:

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

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

A classe cria e depende diretamente da instância MyDependency.The class creates and directly depends on the MyDependency instance. As dependências de código (como no exemplo anterior) são problemáticas e devem ser evitadas por estes motivos:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Para substituir MyDependency por uma implementação diferente, a classe deve ser modificada.To replace MyDependency with a different implementation, the class must be modified.
  • Se MyDependency tiver dependências, elas deverão ser configuradas pela classe.If MyDependency has dependencies, they must be configured by the class. Em um projeto grande com várias classes dependendo da MyDependency, o código de configuração fica pulverizado por todo o aplicativo.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • É difícil testar a unidade dessa implementação.This implementation is difficult to unit test. O aplicativo deve usar uma simulação ou stub da classe MyDependency, o que não é possível com essa abordagem.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

Injeção de dependência trata desses problemas da seguinte maneira:Dependency injection addresses these problems through:

  • Usando uma interface para abstrair a implementação da dependência.The use of an interface to abstract the dependency implementation.
  • Registrando a dependência em um contêiner de serviço.Registration of the dependency in a service container. O ASP.NET Core fornece um contêiner de serviço interno, o IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. Os serviços são registrados no método Startup.ConfigureServices do aplicativo.Services are registered in the app's Startup.ConfigureServices method.
  • Injeção do serviço no construtor da classe na qual ele é usado.Injection of the service into the constructor of the class where it's used. A estrutura assume a responsabilidade de criar uma instância da dependência e de descartá-la quando não for mais necessária.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

No exemplo de aplicativo, a interface IMyDependency define um método que o serviço fornece ao aplicativo:In the sample app, the IMyDependency interface defines a method that the service provides to the app:

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

Essa interface é implementada por um tipo concreto, 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 solicita um ILogger<TCategoryName> em seu construtor.MyDependency requests an ILogger<TCategoryName> in its constructor. Não é incomum usar a injeção de dependência de uma maneira encadeada.It's not unusual to use dependency injection in a chained fashion. Por sua vez, cada dependência solicitada solicita suas próprias dependências.Each requested dependency in turn requests its own dependencies. O contêiner resolve as dependências no grafo e retorna o serviço totalmente resolvido.The container resolves the dependencies in the graph and returns the fully resolved service. O conjunto de dependências que precisa ser resolvido normalmente é chamado de árvore de dependência, grafo de dependência ou grafo de objeto.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency e ILogger<TCategoryName> devem ser registrados no contêiner do serviço.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency está registrado em Startup.ConfigureServices.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> é registrado pela infraestrutura de abstrações de registro em log, portanto, é um serviço fornecido por estrutura registrado por padrão pela estrutura.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

O contêiner resolve ILogger<TCategoryName> aproveitando os tipos abertos (genéricos), eliminando a necessidade de registrar todo tipo construído (genérico):The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type:

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

No exemplo de aplicativo, o serviço IMyDependency está registrado com o tipo concreto MyDependency.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. O registro define o escopo do tempo de vida do serviço para o tempo de vida de uma única solicitação.The registration scopes the service lifetime to the lifetime of a single request. Descreveremos posteriormente neste tópico os tempos de vida do serviço.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>();
}

Observação

Cada método de extensão services.Add{SERVICE_NAME} adiciona (e possivelmente configura) serviços.Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Por exemplo, services.AddMvc() adiciona os serviços exigidos pelo MVC e por Razor Pages.For example, services.AddMvc() adds the services Razor Pages and MVC require. Recomendamos que os aplicativos sigam essa convenção.We recommended that apps follow this convention. Coloque os métodos de extensão no namespace Microsoft.Extensions.DependencyInjection para encapsular grupos de registros de serviço.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Se o construtor do serviço exigir um tipo interno, como string, o tipo poderá ser injetado usando a configuração ou o padrão de opções: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
    }

    ...
}

Uma instância do serviço é solicitada por meio do construtor de uma classe, onde o serviço é usado e atribuído a um campo particular.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. O campo é usado para acessar o serviço conforme o necessário na classe.The field is used to access the service as necessary throughout the class.

No exemplo de aplicativo, a instância IMyDependency é solicitada e usada para chamar o método WriteMessage do serviço: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.");
    }
}

Serviços fornecidos pela estruturaFramework-provided services

O método Startup.ConfigureServices é responsável por definir os serviços usados pelo aplicativo, incluindo recursos de plataforma como o Entity Framework Core e o ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Inicialmente, a IServiceCollection fornecida ao ConfigureServices tem os seguintes serviços definidos (dependendo de como o host foi configurado):Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on how the host was configured):

Tipo de serviçoService Type Tempo de vidaLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryMicrosoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TransitórioTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetimeMicrosoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironmentMicrosoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupMicrosoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterMicrosoft.AspNetCore.Hosting.IStartupFilter TransitórioTransient
Microsoft.AspNetCore.Hosting.Server.IServerMicrosoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryMicrosoft.AspNetCore.Http.IHttpContextFactory TransitórioTransient
Microsoft.Extensions.Logging.ILogger<T>Microsoft.Extensions.Logging.ILogger<T> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactoryMicrosoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderMicrosoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<T>Microsoft.Extensions.Options.IConfigureOptions<T> TransitórioTransient
Microsoft.Extensions.Options.IOptions<T>Microsoft.Extensions.Options.IOptions<T> SingletonSingleton
System.Diagnostics.DiagnosticSourceSystem.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListenerSystem.Diagnostics.DiagnosticListener SingletonSingleton

Quando um método de extensão de coleta do serviço estiver disponível para registrar um serviço (e seus serviços dependentes, se for necessário), a convenção é usar um único método de extensão Add{SERVICE_NAME} para registrar todos os serviços exigidos por esse serviço.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. O código a seguir é um exemplo de como adicionar outros serviços ao contêiner usando os métodos de extensão AddDbContext, AddIdentity e AddMvc:The following code is an example of how to add additional services to the container using the extension methods AddDbContext, AddIdentity, and AddMvc:

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

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

    services.AddMvc();
}

Para saber mais, confira a Classe ServiceCollection na documentação da API.For more information, see the ServiceCollection Class in the API documentation.

Tempos de vida do serviçoService lifetimes

Escolha um tempo de vida apropriado para cada serviço registrado.Choose an appropriate lifetime for each registered service. Os serviços do ASP.NET Core podem ser configurados com os seguintes tempos de vida:ASP.NET Core services can be configured with the following lifetimes:

TransitórioTransient

Serviços temporários de tempo de vida são criados cada vez que são solicitados pelo contêiner de serviço.Transient lifetime services are created each time they're requested from the service container. Esse tempo de vida funciona melhor para serviços leves e sem estado.This lifetime works best for lightweight, stateless services.

Com escopoScoped

Os serviços com tempo de vida com escopo são criados uma vez por solicitação de cliente (conexão).Scoped lifetime services are created once per client request (connection).

Aviso

Ao usar um serviço com escopo em um middleware, injete o serviço no método Invoke ou InvokeAsync.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Não injete por meio de injeção de construtor porque isso força o serviço a se comportar como um singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Para obter mais informações, consulte Middleware do ASP.NET Core.For more information, see Middleware do ASP.NET Core.

SingletonSingleton

Serviços de tempo de vida singleton são criados na primeira solicitação (ou quando ConfigureServices é executado e uma instância é especificada com o registro do serviço).Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Cada solicitação subsequente usa a mesma instância.Every subsequent request uses the same instance. Se o aplicativo exigir um comportamento de singleton, recomendamos permitir que o contêiner do serviço gerencie o tempo de vida do serviço.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Não implemente o padrão de design singleton e forneça o código de usuário para gerenciar o tempo de vida do objeto na classe.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

Aviso

É perigoso resolver um serviço com escopo de um singleton.It's dangerous to resolve a scoped service from a singleton. Pode fazer com que o serviço tenha um estado incorreto durante o processamento das solicitações seguintes.It may cause the service to have incorrect state when processing subsequent requests.

Comportamento da injeção de construtorConstructor injection behavior

Os serviços podem ser resolvidos por dois mecanismos:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities – Permite a criação de objetos sem registro do serviço no contêiner de injeção de dependência.ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilities é usado com abstrações voltadas ao usuário, como Auxiliares de Marca, controladores MVC e associadores de modelo.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Os construtores podem aceitar argumentos que não são fornecidos pela injeção de dependência, mas que precisam atribuir valores padrão.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Quando os serviços são resolvidos por IServiceProvider ou ActivatorUtilities, a injeção do construtor exige um construtor público.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Quando os serviços são resolvidos por ActivatorUtilities, a injeção de construtor exige a existência de apenas de um construtor aplicável.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Há suporte para sobrecargas de construtor, mas somente uma sobrecarga pode existir, cujos argumentos podem ser todos atendidos pela injeção de dependência.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Contextos de Entity FrameworkEntity Framework contexts

Contextos do Entity Framework geralmente são adicionados ao contêiner de serviço usando o tempo de vida com escopo, pois o escopo de operações de banco de dados de aplicativo Web normalmente é para a solicitação do cliente.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. O tempo de vida padrão tem escopo se um tempo de vida não é especificado por uma sobrecarga de AddDbContext<TContext> ao registrar o contexto de banco de dados.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. Serviços com um determinado tempo de vida não devem usar um contexto de banco de dados com um tempo de vida mais curto que aquele do serviço.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Opções de tempo de vida e de registroLifetime and registration options

Para demonstrar a diferença entre as opções de tempo de vida e de registro, considere as interfaces a seguir, que representam tarefas como uma operação com um identificador exclusivo, 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. Dependendo de como o tempo de vida de um serviço de operações é configurado para as interfaces a seguir, o contêiner fornece a mesma instância do serviço, ou outra diferente, quando recebe uma solicitação de uma classe: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
{
}

As interfaces são implementadas na classe Operation.The interfaces are implemented in the Operation class. O construtor Operation gera um GUID, caso um não seja fornecido: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; }
}

Há um OperationService registrado que depende de cada um dos outros Operation tipos.An OperationService is registered that depends on each of the other Operation types. Quando OperationService é solicitado por meio da injeção de dependência, ele recebe a uma nova instância de cada serviço, ou uma instância existente com base no tempo de vida do serviço dependente.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.

  • Quando os serviços temporários forem criados mediante solicitação do contêiner, o OperationId do serviço IOperationTransient será diferente do OperationId do 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 recebe uma nova instância da classe IOperationTransient.OperationService receives a new instance of the IOperationTransient class. A nova instância produz outro OperationId.The new instance yields a different OperationId.
  • Quando os serviços com escopo forem criados por solicitação do cliente, o OperationId do serviço IOperationScoped será o mesmo que o OperationService dentro de uma solicitação do cliente.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. Em todas as solicitações do cliente, os dois serviços compartilham um valor de OperationId diferente.Across client requests, both services share a different OperationId value.
  • Quando os serviços singleton e de instância singleton forem criados uma vez e usados em todas as solicitações de cliente e em todos os serviços, o OperationId será constante entre todas as solicitações de serviço.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; }
}

Em Startup.ConfigureServices, cada tipo é adicionado ao contêiner de acordo com seu tempo de vida nomeado: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>();
}

O serviço IOperationSingletonInstance está usando uma instância específica com uma ID conhecida de Guid.Empty.The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. Fica claro quando esse tipo está em uso (seu GUID contém apenas zeros).It's clear when this type is in use (its GUID is all zeroes).

O exemplo de aplicativo demonstra os tempos de vida do objeto dentro e entre solicitações individuais.The sample app demonstrates object lifetimes within and between individual requests. O exemplo de aplicativo IndexModel solicita cada tipo IOperation e o OperationService.The sample app's IndexModel requests each kind of IOperation type and the OperationService. Depois, a página exibe todos os valores de OperationId da classe de modelo de página e do serviço por meio de atribuições de propriedade: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.");
    }
}

As duas saídas a seguir mostram os resultados das duas solicitações:Two following output shows the results of two requests:

Primeira solicitação:First request:

Operações do controlador:Controller operations:

Transitória: d233e165-f417-469b-a866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Com escopo: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instância: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService operações:OperationService operations:

Transitória: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Com escopo: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instância: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Segunda solicitação:Second request:

Operações do controlador:Controller operations:

Transitória: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Com escopo: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instância: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

OperationService operações:OperationService operations:

Transitória: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Com escopo: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instância: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Observe qual dos valores de OperationId varia em uma solicitação, e entre as solicitações:Observe which of the OperationId values vary within a request and between requests:

  • Os objetos transitórios sempre são diferentes.Transient objects are always different. O valor OperationId transitório da primeira e da segunda solicitações de cliente é diferente para as duas operações OperationService e em todas as solicitações de cliente.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Uma nova instância é fornecida para cada solicitação de serviço e solicitação de cliente.A new instance is provided to each service request and client request.
  • Objetos com escopo são os mesmos em uma solicitação de cliente, mas diferentes entre solicitações de cliente.Scoped objects are the same within a client request but different across client requests.
  • Os objetos singleton são os mesmos para cada objeto e solicitação, independentemente de uma instância Operation ser fornecida em ConfigureServices.Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in ConfigureServices.

Chamar os serviços desde o principalCall services from main

Crie um IServiceScope com IServiceScopeFactory.CreateScope para resolver um serviço com escopo dentro do escopo do aplicativo.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Essa abordagem é útil para acessar um serviço com escopo durante a inicialização para executar tarefas de inicialização.This approach is useful to access a scoped service at startup to run initialization tasks. O exemplo a seguir mostra como obter um contexto para o MyScopedService em Program.Main:The following example shows how to obtain a context for the MyScopedService in Program.Main:

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

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

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

    host.Run();
}

Validação de escopoScope validation

Quando o aplicativo está em execução no ambiente de desenvolvimento, o provedor de serviço padrão executa verificações para saber se:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Os serviços com escopo não são resolvidos direta ou indiretamente pelo provedor de serviço raiz.Scoped services aren't directly or indirectly resolved from the root service provider.
  • Os serviços com escopo não são injetados direta ou indiretamente em singletons.Scoped services aren't directly or indirectly injected into singletons.

O provedor de serviços raiz é criado quando BuildServiceProvider é chamado.The root service provider is created when BuildServiceProvider is called. O tempo de vida do provedor de serviço raiz corresponde ao tempo de vida do aplicativo/servidor quando o provedor começa com o aplicativo e é descartado quando o aplicativo é desligado.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.

Os serviços com escopo são descartados pelo contêiner que os criou.Scoped services are disposed by the container that created them. Se um serviço com escopo é criado no contêiner raiz, o tempo de vida do serviço é promovido efetivamente para singleton, porque ele só é descartado pelo contêiner raiz quando o aplicativo/servidor é desligado.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. A validação dos escopos de serviço detecta essas situações quando BuildServiceProvider é chamado.Validating service scopes catches these situations when BuildServiceProvider is called.

Para obter mais informações, consulte Host da Web do ASP.NET Core.For more information, see Host da Web do ASP.NET Core.

Serviços de solicitaçãoRequest Services

Os serviços disponíveis em uma solicitação do ASP.NET de HttpContext são expostos por meio da coleção HttpContext.RequestServices.The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Os Serviços de Solicitação representam os serviços configurados e solicitados como parte do aplicativo.Request Services represent the services configured and requested as part of the app. Quando os objetos especificam dependências, elas são atendidas pelos tipos encontrados em RequestServices, não em ApplicationServices.When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Em geral, o aplicativo não deve usar essas propriedades diretamente.Generally, the app shouldn't use these properties directly. Em vez disso, solicite os tipos exigidos pelas classes por meio de construtores de classe e permita que a estrutura injete as dependências.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. Isso resulta em classes que são mais fáceis de testar.This yields classes that are easier to test.

Observação

Prefira solicitar dependências como parâmetros de construtor para acessar a coleção RequestServices.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Projetar serviços para injeção de dependênciaDesign services for dependency injection

As melhores práticas são:Best practices are to:

  • Projetar serviços para usar a injeção de dependência a fim de obter suas dependências.Design services to use dependency injection to obtain their dependencies.
  • Evite chamadas de método estático com estado.Avoid stateful, static method calls.
  • Evite a instanciação direta das classes dependentes em serviços.Avoid direct instantiation of dependent classes within services. A instanciação direta acopla o código a uma implementação específica.Direct instantiation couples the code to a particular implementation.
  • Verifique as classes de aplicativo pequenas, bem fatoradas e facilmente testadas.Make app classes small, well-factored, and easily tested.

Se parecer que uma classe tem muitas dependências injetadas, normalmente é um sinal de que a classe tem muitas responsabilidades e está violando o Princípio da Responsabilidade Única (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). Tente refatorar a classe movendo algumas de suas responsabilidades para uma nova classe.Attempt to refactor the class by moving some of its responsibilities into a new class. Lembre-se de que as classes de modelo de página de Razor Pages e as classes do controlador de MVC devem se concentrar em questões de interface do usuário.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Os detalhes de implementação de acesso aos dados e as regras de negócios devem ser mantidos em classes apropriadas a esses interesses separados.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Descarte de serviçosDisposal of services

O contêiner chama Dispose para os tipos IDisposable criados por ele.The container calls Dispose for the IDisposable types it creates. Se uma instância for adicionada ao contêiner pelo código do usuário, ele não será descartado automaticamente.If an instance is added to the container by user code, it isn't disposed automatically.

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

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

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

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

Substituição do contêiner de serviço padrãoDefault service container replacement

O contêiner de serviço interno deve atender às necessidades da estrutura e da maioria dos aplicativos do consumidor.The built-in service container is meant to serve the needs of the framework and most consumer apps. É recomendado usar o contêiner interno, a menos que você precise de um recurso específico que não seja compatível com ele.We recommend using the built-in container unless you need a specific feature that it doesn't support. Alguns dos recursos compatíveis com contêineres de terceiros não encontrados no contêiner interno:Some of the features supported in 3rd party containers not found in the built-in container:

  • Injeção de propriedadeProperty injection
  • Injeção com base no nomeInjection based on name
  • Contêineres filhoChild containers
  • Gerenciamento de tempo de vida personalizadoCustom lifetime management
  • Suporte de Func<T> para inicialização lentaFunc<T> support for lazy initialization

Confira o arquivo readme.md de injeção de dependência para obter uma lista de alguns dos contêineres que dão suporte a adaptadores.See the Dependency Injection readme.md file for a list of some of the containers that support adapters.

O exemplo a seguir substitui o contêiner interno por Autofac:The following sample replaces the built-in container with Autofac:

  • Instale os pacotes de contêiner apropriados:Install the appropriate container package(s):

  • Configure o contêiner no Startup.ConfigureServices e retorne um IServiceProvider:Configure the container in Startup.ConfigureServices and return an IServiceProvider:

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

    Para usar um contêiner de terceiros, Startup.ConfigureServices deve retornar IServiceProvider.To use a 3rd party container, Startup.ConfigureServices must return IServiceProvider.

  • Configure o Autofac em DefaultModule:Configure Autofac in DefaultModule:

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

Em tempo de execução, o Autofac é usado para resolver tipos e injetar dependências.At runtime, Autofac is used to resolve types and inject dependencies. Para saber mais sobre como usar o Autofac com o ASP.NET Core, consulte a documentação do Autofac.To learn more about using Autofac with ASP.NET Core, see the Autofac documentation.

Acesso thread-safeThread safety

Crie serviços singleton thread-safe.Create thread-safe singleton services. Se um serviço singleton tiver uma dependência em um serviço transitório, o serviço transitório também precisará ter acesso thread-safe, dependendo de como ele é usado pelo 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.

O método de fábrica de um único serviço, como o segundo argumento para AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), não precisa ser thread-safe.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. Como um construtor do tipo (static), ele tem garantia de ser chamado uma vez por um único thread.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

RecomendaçõesRecommendations

  • A resolução de serviço baseada em async/await e Task não é compatível.async/await and Task based service resolution is not supported. O C# não é compatível com os construtores assíncronos. Portanto, o padrão recomendado é usar os métodos assíncronos após a resolução síncrona do serviço.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Evite armazenar dados e a configuração diretamente no contêiner do serviço.Avoid storing data and configuration directly in the service container. Por exemplo, o carrinho de compras de um usuário normalmente não deve ser adicionado ao contêiner do serviço.For example, a user's shopping cart shouldn't typically be added to the service container. A configuração deve usar o padrão de opções.Configuration should use the options pattern. Da mesma forma, evite objetos de "suporte de dados" que existem somente para permitir o acesso a outro objeto.Similarly, avoid "data holder" objects that only exist to allow access to some other object. É melhor solicitar o item real por meio da DI.It's better to request the actual item via DI.

  • Evite o acesso estático aos serviços (por exemplo, digitando estaticamente IApplicationBuilder.ApplicationServices para usar em outro lugar).Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • Evite usar o padrão do localizador de serviço.Avoid using the service locator pattern. Por exemplo, não invoque GetService para obter uma instância de serviço quando for possível usar a DI:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    Incorreto:Incorrect:

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

    Correto:Correct:

    private readonly MyOptions _options;
    
    public MyClass(IOptionsMonitor<MyOptions> options)
    {
        _options = options.CurrentValue;
    }
    
    public void MyMethod()
    {
        var option = _options.Option;
    
        ...
    }
    
  • Outra variação de localizador de serviço a ser evitada é injetar um alocador que resolve as dependências em tempo de execução.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Essas duas práticas misturam estratégias de inversão de controle.Both of these practices mix Inversion of Control strategies.

  • Evite o acesso estático a HttpContext (por exemplo, IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Como todos os conjuntos de recomendações, talvez você encontre situações em que é necessário ignorar uma recomendação.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. As exceções são raras —principalmente casos especiais dentro da própria estrutura.Exceptions are rare—mostly special cases within the framework itself.

A DI é uma alternativa aos padrões de acesso a objeto estático/global.DI is an alternative to static/global object access patterns. Talvez você não obtenha os benefícios da DI se combiná-lo com o acesso a objeto estático.You may not be able to realize the benefits of DI if you mix it with static object access.

Recursos adicionaisAdditional resources