ASP .NET Core - Processamento em segundo plano com IHostedService


 Hoje veremos vamos descrever como executar processos em segundo plano com a interface IHostedService e como usar a injeção de dependência para injetar os serviços na ASP .NET Core.

 

Muitos serviços precisam de processamento em segundo plano e a interface IHostedService da ASP.NET Core 2.X fornece um esqueleto de implementação fácil para registrar processos em segundo plano.

Os serviços hospedados são registrados na injeção de dependência na inicialização e iniciados automaticamente. Você não precisa fazer muito malabarismo para rodá-loss na inicialização. Na conclusão você pode implementar um desligamento normal. Ao executar processos em segundo plano, há algumas armadilhas a serem evitadas.

Vamos apresentar a interface IHostedService e como evitar vazamentos de memória comuns ao implementar o serviço hospedado.

Usando IHostedService

Ao implementar a interface IHostedService, você tem dois métodos para implementar:

  1. StartAsync() - é chamado na inicialização
  2. StopAsync() -  é chamado no encerramento

A implementação da classe injetará as dependências necessárias para executar sua lógica de negócios.

 public interface IHostedService
 {
    //  Disparado quando a aplicação host esta pronta para iniciar o serviço
    Task StartAsync(CancellationToken cancellationToken);
   
    //  Disparado quando a aplicação host realizar um shutdown
    Task StopAsync(CancellationToken cancellationToken);
 }

Uma implementação desta interface pode ser incluida no método ConfigureServices da classe Startup :

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddHostedService(); 
}

Quando o serviço é registrado, ele será inicializado quando a inicialização for concluída. Sua implementação do métpdp StartAsync é chamada onde você pode iniciar o processamento.

Iniciar um processo de repetição no IHostedService

Um padrão comum adotado para executar tarefas em segundo plano é:

  • Execute sua lógica
  • Espere algum tempo
  • Verifique se você tem que parar ou repetir o processo

Um exemplo (autor David Fowler) de uma simples implementação de uma classe base pode ser a seguinte:
 
public abstract class BackgroundService : IHostedService
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource(); 
    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Armazena a tarefa que estamos executando
        _executingTask = ExecuteAsync(_stoppingCts.Token); 
        // se a tarefa foi concluida então retorna,
        // isto causa o cancelamento e a falha do chamados
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        } 
        // de outra forma ela esta rodando
        return Task.CompletedTask;
    } 
    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Para a chamada sem iniciar
        if (_executingTask == null)
        {
            return;
        } 
        try
        {
            // Sinaliza o cancelamento para o método
            _stoppingCts.Cancel();
        }
        finally
        {
            // Aguarde ate que a tarefa termine ou que pare
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,cancellationToken));
        }
    }

    protected virtual async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        do
        {
            await Process();
            //aguarde 5 segundos
            await Task.Delay(5000, stoppingToken); 
        }
        while (!stoppingToken.IsCancellationRequested);
    }
     protected abstract Task Process();
}

Essa implementação manipula um desligamento normal da tarefa em segundo plano. Você só precisa implementar o método Process() para concluir seu trabalho.

As implementações do IHostedService são singletons em seu aplicativo e ao injetar uma dependência com scoped ou transient, eles terão a mesma vida útil do IHostedService e isso não é a intençao.

Felizmente, quando rodando em modo de desenvolvimento, o injetor de dependência impedirá sua execução. Ele verifica se a dependência esta em escopo e, em seguida, lança uma exceção.

O erro impede que você execute um objeto com escopo definido (por exemplo, um DbContext) em um singleton. Para contornar isso, você pode usar a interface IServiceScopeFactory e criar um escopo próprio para cada vez que você estiver executando o processo. O escopo cuida de todos os objetos com escopo definido e IDisposable criados para o seu processamento.

Dessa forma a interface IHostedServices oferece uma maneira simples de implementar serviços em segundo plano com um desligamento elegante.

Somente esteja atento ao fato de que neste cenário você gerencia objetos que não são singleton e que são criados pelo injetor de dependência com um escopo.

Em outro artigo veremos um exemplo prático de utilização de IHostedService.

"Meus filhinhos, estas coisas vos escrevo, para que não pequeis; e, se alguém pecar, temos um Advogado para com o Pai, Jesus Cristo, o justo."
1 João 2:1

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?


Referências:


José Carlos Macoratti