C# -
Dependency Injection via Console .NET Core - II
![]() |
Neste artigo veremos realizar a injeção de dependência em uma aplicação Console .NET Core usando o contêiner nativo da ASP .NET Core abordando os tempos de vida dos serviços. |
Na primeira parte do artigo vimos como usar o contêiner de injeção de dependência do .NET Core em uma aplicação Console.
Veremos agora o conceito de tempo de vida dos serviços injetados.
A vida útil da dependência (ou vida útil do serviço; usarei as palavras "dependência" e "serviço" de forma intercambiável neste artigo) define quando uma dependência é instanciada e por quanto tempo ela dura.
O contêiner de injeção de dependência (DI) é responsável
por controlar isso. Tradicionalmente, nas versões anteriores do ASP.NET, os
desenvolvedores tinham que usar bibliotecas de terceiros, como o
Autofac, para
fazer isso. No ASP.NET Core, um simples contêiner de DI é incorporado e não
requer muita configuração (e pode ser substituído, se for necessário).
O sistema de injeção de dependência criado no ASP.NET Core nos permite definir
as regras de reutilização de instâncias de serviços. Atualmente existem três
opções disponíveis:
1 - Singleton - Uma única instância da
classe de serviço é criada, armazenada na memória e reutilizada para todas as
injeções. Esta opção pode ser usado para serviços 'custosos' de
instanciar como um banco de dados.
Observe que a instância é mantida na memória durante toda
a vida útil do aplicativo, portanto, observe o uso de memória. O lado positivo
desta opção é que a memória será alocada apenas uma vez, portanto o coletor de
lixo terá menos trabalho.
2- Scoped - Cria uma instância do serviço
por solicitação do cliente ou request. Podemos dizer que esta opção funciona
como o Singleton por solicitação. Todos os
middlewares, controladores MVC etc. que participam do tratamento de uma única
solicitação obterão a mesma instância. Podemos usar essa opção com o contexto do
Entity Framework.
Atenção, ao usar um serviço com esta opção definido em um
middleware, injete o serviço no método Invoke ou
InvokeAsync. Não injete via injeção de construtor,
porque força o serviço a se comportar como um singleton.
3- Transient -
São criados sempre
que solicitados no contêiner de serviço, ou seja
sempre que o serviço é resolvido a partir de um contêiner de DI, uma nova
instância é criada. Isso pode causar alocações e desalocações freqüentes de
memória e, portanto, pode ter um impacto negativo no desempenho, se usado com
muita frequência. Por isso é recomendado ser usado com serviços leves e sem
estado.
A seguir veremos um exemplo prático de funcionamento deste recurso em uma aplicação Console.
Tempos de vidas do serviço em uma aplicação Console
Crie um projeto Console do tipo .NET Core no VS 2019 ou no VS Code.
A seguir inclua o seguinte pacote no seu projeto:
pacote Microsoft.Extensions.DependencyInjection;
A título de exemplo vamos criar as classes para definir cada tempo de vida de um serviço:
Crie uma pasta Services no projeto e nesta pasta crie as classes abaixo:
1- SingletonOperacao
class SingletonOperacao
{
public SingletonOperacao()
{
Console.WriteLine("Serviço Singleton foi criado");
}
}
|
2- ScopedOperacao
class ScopedOperacao
{
public ScopedOperacao()
{
Console.WriteLine("Serviço Scoped foi criado");
}
}
|
3- TransientOperacao
class TransientOperacao
{
public TransientOperacao()
{
Console.WriteLine("Serviço Transient foi criado");
}
}
|
Agora vamos usar o contêiner de injeção de dependência nativo para criar ServiceProvider , registrar os serviços e obter os serviços com diferentes tempos de vida.
Para isso inclua o código a seguir no método Main() da classe Program:
using CShp_ConsoleLifeTimeDI.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
class Program
{
static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddTransient<TransientOperacao>();
services.AddScoped<ScopedOperacao>();
services.AddSingleton<SingletonOperacao>();
var serviceProvider = services.BuildServiceProvider();
Console.WriteLine("-------- Primeiro Request --------\n");
var transientService = serviceProvider.GetService<TransientOperacao>();
var scopedService = serviceProvider.GetService<ScopedOperacao>();
var singletonService = serviceProvider.GetService<SingletonOperacao>();
Console.WriteLine();
Console.WriteLine("-------- Segundo Request --------\n");
var transientService2 = serviceProvider.GetService<TransientOperacao>();
var scopedService2 = serviceProvider.GetService<ScopedOperacao>();
var singletonService2 = serviceProvider.GetService<SingletonOperacao>();
Console.WriteLine();
Console.WriteLine(new String('-',30));
Console.ReadLine();
}
}
}
|
Neste código criamos as instâncias dos serviços duas vezes para ver qual serviço seria recriado.
O resultado pode ser visto na figura abaixo:
Como vemos ao criar os serviços pela segunda vez apenas o serviço Transient é recriado, já os serviços com tempo de vida definidos como Singleton e Scoped não são criados novamente.
Vamos agora criar outro exemplo onde vamos definir a primeira e a segunda instância do serviço no mesmo escopo.
Vamos alterar o código conforme abaixo:
private static void Demo2() { IServiceCollection services = new ServiceCollection(); services.AddTransient<TransientOperacao>();
services.AddScoped<ScopedOperacao>();
services.AddSingleton<SingletonOperacao>();
var serviceProvider = services.BuildServiceProvider();
Console.WriteLine();
Console.WriteLine("-------- Primeiro Request --------");
using (var scope = serviceProvider.CreateScope())
{
var transientService = scope.ServiceProvider.GetService<TransientOperacao>();
var scopedService = scope.ServiceProvider.GetService<ScopedOperacao>();
var singletonService = scope.ServiceProvider.GetService<SingletonOperacao>();
}
Console.WriteLine();
Console.WriteLine("-------- Segundo Request --------");
using (var scope = serviceProvider.CreateScope())
{
var transientService = scope.ServiceProvider.GetService<TransientOperacao>();
var scopedService = scope.ServiceProvider.GetService<ScopedOperacao>();
var singletonService = scope.ServiceProvider.GetService<SingletonOperacao>();
}
Console.WriteLine();
Console.WriteLine("-----------------------------");
}
|
Estamos usando o método CreateScope que cria um novo IServiceScope que usamos para resolver serviços com escopo.
A camada de DI específica tem uma interface chamada IServiceScopeFactory para permitir criar um escopo para a injeção de dependência. Chamar CreateScope retorna um IServiceScope que implementa IDisposable. Portanto usamos uma declaração 'using' para proteger sua destruição (e encerrar o escopo):
Executando o projeto iremos obter o seguinte resultado:
Como você pode
ver, a instância de serviço definido como Scoped é
criada mais de uma vez em escopos diferentes.
Na próxima parte do artigo veremos o registro de instância, o registro de tipo
genérico e o registro múltiplo.
Pegue o projeto
aqui:
CShp_ConsoleLifeTimeDI.zip
Tu, pois, meu filho,
fortifica-te na graça que há em Cristo Jesus.
2 Timóteo 2:1
Referências: