Compartilhar via


Início Rápido: Noções básicas sobre injeção de dependência no .NET

Neste guia de introdução rápida, você cria um aplicativo de console do .NET que cria manualmente um ServiceCollection e o correspondente ServiceProvider. Você aprenderá a registrar serviços e resolvê-los usando a DI (injeção de dependência). Este artigo usa o pacote NuGet Microsoft.Extensions.DependencyInjection para demonstrar os conceitos básicos do DI no .NET.

Observação

Este artigo não aproveita os recursos genéricos do host . Para obter um guia mais abrangente, consulte Usar a injeção de dependência no .NET.

Introdução

Para começar, crie um novo aplicativo de console do .NET chamado DI.Basics. Algumas das abordagens mais comuns para criar um projeto de console são referenciadas na seguinte lista:

Você precisa adicionar a referência de pacote ao Microsoft.Extensions.DependencyInjection no arquivo de projeto. Independentemente da abordagem, verifique se o projeto é semelhante ao seguinte XML do arquivo DI.Basics.csproj :

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
  </ItemGroup>

</Project>

Noções básicas sobre injeção de dependência

A injeção de dependência é um padrão de design que permite remover dependências codificadas e tornar seu aplicativo mais mantenedível e testável. A DI é uma técnica para alcançar a Inversão de Controle (IoC) entre classes e suas dependências.

As abstrações para DI no .NET são definidas no pacote NuGet Microsoft.Extensions.DependencyInjection.Abstractions :

  • IServiceCollection: define um contrato para uma coleção de descritores de serviço.
  • IServiceProvider: define um mecanismo para recuperar um objeto de serviço.
  • ServiceDescriptor: descreve um serviço com seu tipo de serviço, implementação e tempo de vida.

No .NET, o DI é gerenciado adicionando serviços e configurando-os em um IServiceCollection. Depois que os serviços são registrados, uma IServiceProvider instância é criada chamando o BuildServiceProvider método. Ele IServiceProvider atua como um contêiner de todos os serviços registrados e é usado para resolver serviços.

Criar serviços de exemplo

Nem todos os serviços são criados da mesma forma. Alguns serviços exigem uma nova instância sempre que o contêiner de serviço os obtém (transitório), enquanto outros devem ser compartilhados entre solicitações (escopo) ou durante todo o tempo de vida do aplicativo (singleton). Para obter mais informações sobre o tempo de vida do serviço, consulte os tempos de vida do serviço.

Da mesma forma, alguns serviços expõem apenas um tipo concreto, enquanto outros são expressos como um contrato entre uma interface e um tipo de implementação. Você cria várias variações de serviços para ajudar a demonstrar esses conceitos.

Crie um novo arquivo C# chamado IConsole.cs e adicione o seguinte código:

public interface IConsole
{
    void WriteLine(string message);
}

Esse arquivo define uma IConsole interface que expõe um único método. WriteLine Em seguida, crie um novo arquivo C# chamado DefaultConsole.cs e adicione o seguinte código:

internal sealed class DefaultConsole : IConsole
{
    public bool IsEnabled { get; set; } = true;

    void IConsole.WriteLine(string message)
    {
        if (IsEnabled is false)
        {
            return;
        }

        Console.WriteLine(message);
    }
}

O código anterior representa a implementação padrão da IConsole interface. O método WriteLine grava condicionalmente no console com base na propriedade IsEnabled.

Dica

A nomenclatura de uma implementação é uma escolha em que sua equipe de desenvolvimento deve concordar. O Default prefixo é uma convenção comum para indicar uma implementação padrão de uma interface, mas não é necessária.

Em seguida, crie um arquivo IGreetingService.cs e adicione o seguinte código C#:

public interface IGreetingService
{
    string Greet(string name);
}

Em seguida, adicione um novo arquivo C# chamado DefaultGreetingService.cs e adicione o seguinte código:

internal sealed class DefaultGreetingService(
    IConsole console) : IGreetingService
{
    public string Greet(string name)
    {
        var greeting = $"Hello, {name}!";

        console.WriteLine(greeting);

        return greeting;
    }
}

O código anterior representa a implementação padrão da IGreetingService interface. A implementação do serviço requer IConsole como um parâmetro de construtor primário. O método Greet:

  • Cria um greeting dado o name.
  • Chama o método WriteLine na instância IConsole.
  • Retorna o greeting ao chamador.

O último serviço a ser criado é o arquivo FarewellService.cs , adicione o seguinte código C# antes de continuar:

public class FarewellService(IConsole console)
{
    public string SayGoodbye(string name)
    {
        var farewell = $"Goodbye, {name}!";

        console.WriteLine(farewell);

        return farewell;
    }
}

Representa FarewellService um tipo concreto, não uma interface. Ele deve ser declarado public para torná-lo acessível aos consumidores. Ao contrário de outros tipos de implementação de serviço que foram declarados como internal e sealed, esse código demonstra que nem todos os serviços precisam ser interfaces. Ele também mostra que as implementações de serviço podem ser sealed para impedir a herança e internal para restringir o acesso ao assembly.

Atualizar a Program classe

Abra o arquivo Program.cs e substitua o código existente pelo seguinte código C#:

using Microsoft.Extensions.DependencyInjection;

// 1. Create the service collection.
var services = new ServiceCollection();

// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    });
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();

// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();

// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();

// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");

O código atualizado anterior demonstra o "como fazer":

  • Crie uma nova ServiceCollection instância.
  • Registre e configure serviços no ServiceCollection:
    • Usando a sobrecarga de implementação de fábrica IConsole, retorne um tipo DefaultConsole com IsEnabled definido como true.
    • O IGreetingService é adicionado com um tipo de implementação correspondente do tipo DefaultGreetingService.
    • O FarewellService é adicionado como um tipo concreto.
  • Compile a ServiceProvider partir do ServiceCollection.
  • Resolva os serviços IGreetingService e FarewellService.
  • Use os serviços resolvidos para saudar e dizer adeus a uma pessoa chamada David.

Se você atualizar a propriedade IsEnabled do DefaultConsole para false, os métodos Greet e SayGoodbye omitirão a gravação das mensagens resultantes no console. Uma alteração como essa ajuda a demonstrar que o IConsole serviço é injetado nos serviços IGreetingService e FarewellService como uma dependência que influencia o comportamento das aplicações.

Todos esses serviços são registrados como singletons, embora para este exemplo, ele funcione de forma idêntica se fossem registrados como serviços transitórios ou escopados.

Importante

Neste exemplo inventado, os tempos de vida do serviço não importam, mas em um aplicativo do mundo real, você deve considerar cuidadosamente o tempo de vida de cada serviço.

Executar o aplicativo de exemplo

Para executar o aplicativo de exemplo, pressione F5 no Visual Studio ou no Visual Studio Code ou execute o dotnet run comando no terminal. Quando o aplicativo for concluído, você deverá ver a seguinte saída:

Hello, David!
Goodbye, David!

Descritores de serviço

As APIs mais usadas para adicionar serviços ao ServiceCollection são métodos de extensão genéricos nomeados conforme a duração de vida, como:

  • AddSingleton<TService>
  • AddTransient<TService>
  • AddScoped<TService>

Esses são métodos de conveniência que criam uma instância ServiceDescriptor e a adicionam ao ServiceCollection. É ServiceDescriptor uma classe simples que descreve um serviço com seu tipo de serviço, tipo de implementação e tempo de vida. Ele também pode descrever fábricas e instâncias de implementação.

Para cada um dos serviços que você registrou no ServiceCollection, você pode optar por chamar diretamente a instância ServiceDescriptor com o método Add. Considere os seguintes exemplos:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));

O código anterior é equivalente a como o IConsole serviço foi registrado no ServiceCollection. O Add método é usado para adicionar uma ServiceDescriptor instância que descreve o IConsole serviço. O método ServiceDescriptor.Describe estático delega a vários ServiceDescriptor construtores. Considere o código equivalente para o IGreetingService serviço:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IGreetingService),
    implementationType: typeof(DefaultGreetingService),
    lifetime: ServiceLifetime.Singleton));

O código anterior descreve o serviço com seu IGreetingService tipo de serviço, tipo de implementação e tempo de vida. Por fim, considere o código equivalente para o FarewellService serviço:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(FarewellService),
    implementationType: typeof(FarewellService),
    lifetime: ServiceLifetime.Singleton));

O código anterior descreve o tipo concreto FarewellService como os tipos de serviço e implementação. O serviço está registrado como um serviço singleton.

Consulte também