Tutorial: Usar a injeção de dependência no .NET
Este tutorial mostra como usar a injeção de dependência (DI) no .NET. Com o Microsoft Extensions, o DI é gerenciado adicionando serviços e configurando-os em um IServiceCollectionarquivo . A IHost interface expõe a IServiceProvider instância, que atua como um contêiner de todos os serviços registrados.
Neste tutorial, irá aprender a:
- Criar um aplicativo de console .NET que usa injeção de dependência
- Criar e configurar um host genérico
- Escrever várias interfaces e implementações correspondentes
- Use a vida útil e o escopo do serviço para DI
Pré-requisitos
- SDK do .NET Core 3.1 ou posterior.
- Familiaridade com a criação de novos aplicativos .NET e a instalação de pacotes NuGet.
Criar um novo aplicativo de console
Usando o comando dotnet new ou um assistente de novo projeto IDE, crie um novo aplicativo de console .NET chamado ConsoleDI..Example Adicione o pacote NuGet Microsoft.Extensions.Hosting ao projeto.
Seu novo arquivo de projeto de aplicativo de console deve ser semelhante ao seguinte:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ConsoleDI.Example</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
</Project>
Importante
Neste exemplo, o pacote NuGet Microsoft.Extensions.Hosting é necessário para criar e executar o aplicativo. Alguns metapacotes podem conter o Microsoft.Extensions.Hosting
pacote, caso em que uma referência explícita do pacote não é necessária.
Adicionar interfaces
Neste aplicativo de exemplo, você aprenderá como a injeção de dependência lida com a vida útil do serviço. Você criará várias interfaces que representam diferentes tempos de vida de serviço. Adicione as seguintes interfaces ao diretório raiz do projeto:
IReportServiceLifetime.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IReportServiceLifetime
{
Guid Id { get; }
ServiceLifetime Lifetime { get; }
}
A IReportServiceLifetime
interface define:
- Uma
Guid Id
propriedade que representa o identificador exclusivo do serviço. - Uma ServiceLifetime propriedade que representa o tempo de vida do serviço.
IExampleTransientServiço.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleTransientService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}
IExampleScopedServiço.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleScopedService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}
IExampleSingletonServiço.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleSingletonService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}
Todas as subinterfaces de IReportServiceLifetime
implementar explicitamente o IReportServiceLifetime.Lifetime
com um padrão. Por exemplo, IExampleTransientService
implementa IReportServiceLifetime.Lifetime
explicitamente com o ServiceLifetime.Transient
valor.
Adicionar implementações padrão
Todas as implementações de exemplo inicializam suas Id
propriedades com o resultado de Guid.NewGuid(). Adicione as seguintes classes de implementação padrão para os vários serviços ao diretório raiz do projeto:
ExampleTransientServiço.cs
namespace ConsoleDI.Example;
internal sealed class ExampleTransientService : IExampleTransientService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleScopedServiço.cs
namespace ConsoleDI.Example;
internal sealed class ExampleScopedService : IExampleScopedService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleSingletonServiço.cs
namespace ConsoleDI.Example;
internal sealed class ExampleSingletonService : IExampleSingletonService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
Cada implementação é definida como internal sealed
e implementa sua interface correspondente. Por exemplo, ExampleSingletonService
implementa IExampleSingletonService
.
Adicionar um serviço que requer DI
Adicione a seguinte classe de repórter de tempo de vida do serviço, que atua como um serviço para o aplicativo de console:
ServiceLifetimeReporter.cs
namespace ConsoleDI.Example;
internal sealed class ServiceLifetimeReporter(
IExampleTransientService transientService,
IExampleScopedService scopedService,
IExampleSingletonService singletonService)
{
public void ReportServiceLifetimeDetails(string lifetimeDetails)
{
Console.WriteLine(lifetimeDetails);
LogService(transientService, "Always different");
LogService(scopedService, "Changes only with lifetime");
LogService(singletonService, "Always the same");
}
private static void LogService<T>(T service, string message)
where T : IReportServiceLifetime =>
Console.WriteLine(
$" {typeof(T).Name}: {service.Id} ({message})");
}
O ServiceLifetimeReporter
define um construtor que requer cada uma das interfaces de serviço acima mencionadas, ou seja, , IExampleTransientService
, IExampleScopedService
e IExampleSingletonService
. O objeto expõe um único método que permite ao consumidor relatar o serviço com um determinado lifetimeDetails
parâmetro. Quando invocado, o método registra o ReportServiceLifetimeDetails
identificador exclusivo de cada serviço com a mensagem de tempo de vida do serviço. As mensagens de log ajudam a visualizar o tempo de vida do serviço.
Registrar serviços para DI
Programa de atualização .cs com o seguinte código:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();
using IHost host = builder.Build();
ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");
await host.RunAsync();
static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
using IServiceScope serviceScope = hostProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine("...");
logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine();
}
Cada services.Add{LIFETIME}<{SERVICE}>
método de extensão adiciona (e potencialmente configura) serviços. Recomendamos que as aplicações sigam esta convenção. Não coloque métodos de extensão no namespace, a Microsoft.Extensions.DependencyInjection menos que você esteja criando um pacote oficial da Microsoft. Métodos de extensão que são definidos dentro do Microsoft.Extensions.DependencyInjection
namespace:
- São exibidos no IntelliSense sem a necessidade de blocos adicionais
using
. - Reduza o número de instruções necessárias
using
nasProgram
classes ouStartup
onde esses métodos de extensão são normalmente chamados.
A aplicação:
- Cria uma IHostBuilder instância com as configurações do construtor de hosts.
- Configura serviços e os adiciona com o tempo de vida correspondente do serviço.
- Chama Build() e atribui uma instância de IHost.
- Chamadas
ExemplifyScoping
, passando no IHost.Services.
Conclusão
Neste aplicativo de exemplo, você criou várias interfaces e implementações correspondentes. Cada um desses serviços é identificado exclusivamente e emparelhado com um ServiceLifetimearquivo . O aplicativo de exemplo demonstra como registrar implementações de serviço em uma interface e como registrar classes puras sem interfaces de backup. O aplicativo de exemplo demonstra como as dependências definidas como parâmetros do construtor são resolvidas em tempo de execução.
Quando você executa o aplicativo, ele exibe uma saída semelhante à seguinte:
// Sample output:
// Lifetime 1: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 1: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
//
// Lifetime 2: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 2: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
Na saída do aplicativo, você pode ver que:
- Transient Os serviços são sempre diferentes, uma nova instância é criada a cada recuperação do serviço.
- Scoped Os serviços mudam apenas com um novo escopo, mas são a mesma instância dentro de um escopo.
- Singleton Os serviços são sempre os mesmos, uma nova instância só é criada uma vez.
Consulte também
- Diretrizes de injeção de dependência
- Dependency injection in ASP.NET Core (Injeção de dependências no ASP.NET Core)