Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Neste início rápido, cria uma aplicação de consola .NET que cria manualmente um ServiceCollection e um ServiceProvider correspondente. Aprende a registar serviços e resolvê-los usando injeção de dependências (DI). Este artigo utiliza o pacote NuGet Microsoft.Extensions.DependencyInjection para demonstrar os fundamentos do DI em .NET.
Observação
Este artigo não tira partido das funcionalidades genéricas do anfitrião . Para um guia mais abrangente, veja Usar injeção de dependência em .NET.
Introdução
Para começar, crie uma nova aplicação de consola .NET chamada DI.Basics. Algumas das abordagens mais comuns para criar um projeto de consola são referenciadas na seguinte lista:
- Visual Studio: Arquivo > Novo > Projeto.
- Visual Studio Code e a extensão do C# Dev Kit: opção de menu Explorador de Soluções .
-
.NET CLI: Para executar
dotnet new consolecomando no terminal.
Tens de adicionar a referência do pacote ao Microsoft.Extensions.DependencyInjection no ficheiro do projeto. Independentemente da abordagem, certifique-se de que o projeto se assemelha ao seguinte XML do ficheiro 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 de injeção de dependência
A injeção de dependências é um padrão de design que permite remover dependências codificadas e tornar a sua aplicação mais sustentável e testável. DI é uma técnica para alcançar a Inversão de Controlo (IoC) entre classes e as suas dependências.
As abstrações para DI em .NET estã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 o seu tipo de serviço, implementação e vida útil.
No .NET, a DI é gerida adicionando serviços e configurando-os num IServiceCollectionarquivo . Depois de os serviços serem registados, uma IServiceProvider instância é construída chamando o BuildServiceProvider método. O IServiceProvider atua como um contentor de todos os serviços registados e é usado para resolver os serviços.
Criar serviços de exemplo
Nem todos os serviços são iguais. Alguns serviços requerem uma nova instância cada vez que o contentor de serviço os recebe (transitório), enquanto outros devem ser partilhados entre pedidos (com escopo) ou durante toda a vida útil da aplicação (singleton). Para mais informações sobre a duração de vida do serviço, consulte duração de vida do serviço.
Da mesma forma, alguns serviços apenas expõem um tipo concreto, enquanto outros são expressos como um contrato entre uma interface e um tipo de implementação. Cria várias variações de serviços para ajudar a demonstrar estes conceitos.
Crie um novo ficheiro C# chamado IConsole.cs e adicione o seguinte código:
public interface IConsole
{
void WriteLine(string message);
}
Este ficheiro define uma IConsole interface que expõe um único método, WriteLine. De seguida, crie um novo ficheiro 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 escreve na consola condicionalmente com base na propriedade IsEnabled.
Sugestão
A nomeação de uma implementação é uma escolha com que a sua equipa de desenvolvimento deve concordar. O Default prefixo é uma convenção comum para indicar uma implementação padrão de uma interface, mas não é obrigatório.
De seguida, crie um ficheiro IGreetingService.cs e adicione o seguinte código C#:
public interface IGreetingService
{
string Greet(string name);
}
Depois adiciona um novo ficheiro C# chamado DefaultGreetingService.cs e adiciona 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 um IConsole como parâmetro construtor primário. O método Greet:
- Cria um
greetingdado oname. - Chama o método
WriteLinena instânciaIConsole. - Devolve-o
greetingao chamador.
O último serviço a criar é o ficheiro 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;
}
}
O FarewellService representa um tipo concreto, não uma interface. Deve ser declarado como public para torná-lo acessível aos consumidores. Ao contrário de outros tipos de implementação de serviços que foram declarados como internal e sealed, este código demonstra que nem todos os serviços precisam de ser interfaces. Mostra também que as implementações de serviços podem servir sealed para evitar herança e internal restringir o acesso à assembleia.
Atualizar a Program classe
Abra o ficheiro 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 procedimento prático:
- Cria uma nova
ServiceCollectioninstância. - Registar e configurar serviços nos
ServiceCollection:- Usando a fábrica de implementação com sobrecarga
IConsole, retorna um tipoDefaultConsolecom oIsEnableddefinido paratrue. - O
IGreetingServiceé adicionado com um tipo de implementação correspondenteDefaultGreetingService. - O
FarewellServiceé adicionado como um tipo concreto.
- Usando a fábrica de implementação com sobrecarga
- Constrói o
ServiceProvidera partir doServiceCollection. - Resolva os serviços
IGreetingServiceeFarewellService. - Use os serviços disponíveis para cumprimentar e despedir-se de uma pessoa chamada
David.
Se atualizar a IsEnabled propriedade do DefaultConsole para false, o método Greet e os métodos SayGoodbye omitem a escrita das mensagens resultantes para a consola. Uma alteração deste tipo ajuda a demonstrar que o IConsole serviço é injetado nos serviços IGreetingService e FarewellService como uma dependência que influencia o comportamento dessas aplicações.
Todos estes serviços estão registados como singletons, embora, para esta amostra, funcione de forma idêntica se forem registados como serviços transitórios ou com âmbito .
Importante
Neste exemplo artificial, a duração do serviço não importa, mas numa aplicação real, deve considerar cuidadosamente a vida útil de cada serviço.
Executar o aplicativo de exemplo
Para executar a aplicação de exemplo, pressione F5 no Visual Studio ou Visual Studio Code, ou execute o dotnet run comando no terminal. Quando a aplicação estiver concluída, deverá ver o seguinte resultado:
Hello, David!
Goodbye, David!
Descritores de serviço
As APIs mais frequentemente usadas para adicionar serviços ao ServiceCollection são métodos de extensão genéricos associados a tempos de vida específicos, tais como:
AddSingleton<TService>AddTransient<TService>AddScoped<TService>
Estes métodos são métodos de conveniência que criam uma ServiceDescriptor instância e a adicionam ao ServiceCollection. É ServiceDescriptor uma classe simples que descreve um serviço com o seu tipo de serviço, tipo de implementação e tempo de vida. Também pode descrever fábricas e instâncias de implementação.
Para cada um dos serviços que registou no ServiceCollection, pode ligar diretamente para o método Add com uma instância ServiceDescriptor. 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 à forma como o IConsole serviço foi registado 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 IGreetingService serviço com o seu tipo de serviço, tipo de implementação e tempo de vida. Finalmente, 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 específico FarewellService tanto como tipo de serviço quanto como tipo de implementação. O serviço está registado como serviço singleton.