Módulo 5 · Worker Service — Capítulo 02

IHostedService e BackgroundService

A peça central de todo Worker Service: a interface que o .NET usa para saber que algo precisa iniciar junto com a aplicação e parar de forma graciosa quando ela encerra.

1. IHostedService — o contrato mínimo

Qualquer classe registrada como "hosted service" precisa implementar dois métodos: StartAsync (chamado quando a aplicação inicia) e StopAsync (chamado quando ela está encerrando):

C# IHostedService — a interface crua
public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

Na prática, você raramente implementa IHostedService diretamente — a classe abstrata BackgroundService já cuida da parte repetitiva (começar, parar, tratar cancelamento) e expõe um único método para você preencher:

2. BackgroundService — o ponto de partida real

C# Worker.cs — gerado pelo template
public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger) => _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker executando às {Hora}", DateTimeOffset.Now);
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
}
PeçaPapel
ExecuteAsyncOnde toda a lógica de fundo mora — chamado uma vez, e deveria conter o laço de repetição
stoppingTokenSinalizado quando a aplicação está sendo encerrada — checá-lo é o que permite parar graciosamente
Task.Delay(..., stoppingToken)Pausa entre execuções, mas cancelável imediatamente se o token disparar (em vez de esperar o delay inteiro)
Atenção

Sempre passe stoppingToken para Task.Delay e para qualquer chamada assíncrona dentro do laço (incluindo chamadas ao SQL Server). Sem isso, a aplicação pode demorar muito mais que o esperado para encerrar quando o processo recebe um pedido de parada (por exemplo, durante um deploy).

3. Registrando o worker

C# Program.cs
var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<Worker>();
builder.Services.AddScoped<IPedidoRepository>(_ =>
    new PedidoRepository(builder.Configuration.GetConnectionString("Default")!));

var host = builder.Build();
host.Run();

AddHostedService<Worker>() é o registro que diz ao .NET "inicie esta classe junto com a aplicação". É possível registrar múltiplos hosted services no mesmo processo — cada um roda seu próprio ExecuteAsync de forma independente.

4. Tratamento de erro dentro do laço

Uma exceção não tratada dentro de ExecuteAsync derruba o worker inteiro — diferente de uma exceção numa Action de API (Módulo 4, capítulo 6), que afeta só aquela requisição. Por isso, o laço de um worker precisa do próprio tratamento de erro:

C# protegendo o laço contra falhas isoladas
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        try
        {
            await ProcessarPedidosPendentesAsync(stoppingToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Falha ao processar pedidos pendentes");
            // não relança — uma falha nesta execução não deveria matar o worker inteiro
        }

        await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
    }
}
Conectando com o Módulo 1

Este é um dos poucos lugares legítimos onde capturar Exception genericamente e não relançar (capítulo 8 do Módulo 1) faz sentido — o objetivo aqui é justamente isolar uma falha de uma execução para que o processo inteiro continue vivo e tente de novo no próximo ciclo. A regra "não engula exceções" continua valendo: sempre logue.

5. Onde isso te leva

Com a estrutura básica de execução contínua dominada, o próximo capítulo cobre como injetar dependências corretamente dentro de um worker (um detalhe diferente do MVC/API, por causa do tempo de vida do próprio serviço) e como configurar logging estruturado.

📌 Resumo do capítulo

  • IHostedService é o contrato mínimo (Start/Stop); BackgroundService é a classe base prática, com um único método ExecuteAsync a implementar.
  • Sempre respeite o stoppingToken em delays e chamadas assíncronas, para permitir encerramento gracioso.
  • Uma exceção não tratada em ExecuteAsync derruba o worker inteiro — proteja o laço com try/catch e log, sem deixar o processo morrer por uma falha isolada.

✏️ Praticando

  1. Implemente um Worker que loga uma mensagem a cada 10 segundos, respeitando o stoppingToken.
  2. Force uma exceção dentro do laço e confirme que, com o try/catch, o worker continua rodando no ciclo seguinte em vez de encerrar.