Eseguire task temporizzati tramite hosted service in ASP.NET Core

di Marco De Sanctis, in ASP.NET Core,

Spesso nelle nostre applicazioni, ci troviamo a dover eseguire operazioni a intervalli regolari, per esempio per verificare se un utente è ancora connesso, pulire il contenuto di una tabella o effettuare il polling su una risorsa.

Una possibile soluzione, se siamo su Azure, è quella di utilizzare una Function con time trigger, con la complessità però di avere un'ulteriore "moving part" nella nostra architettura.

Un'alternativa più semplice è sfruttare gli hosted service di ASP.NET Core, che vengono eseguiti in automatico dal runtime come processi di background.

Ciò che dobbiamo fare è realizzare una classe che implementi l'interfaccia IHostedService:

internal class MyTimedService : IHostedService, IDisposable
{
  private Timer _timer;
  private ILogger<MyTimedService> _logger;

  public MyTimedService(ILogger<MyTimedService> logger)
  {
    _logger = logger ?? throw new ArgumentNullException("logger");
  }

  public Task StartAsync(CancellationToken cancellationToken)
  {
    _timer = new Timer(
      async state => 
      {
        try
        {
          cancellationToken.ThrowIfCancellationRequested();
          // ... nostra logica qui ...
        }
        catch (Exception ex)
        {
          _logger.LogError(ex, "Failed to execute");
        }
      },
      state:null,
      dueTime:TimeSpan.Zero, // questo è il delay per la prima esecuzione
      period:TimeSpan.FromMinutes(1)); // ogni minuto

    return Task.CompletedTask;
  }

  public Task StopAsync(CancellationToken cancellationToken)
  {
    _timer.Change(Timeout.Infinite, Timeout.Infinite);
    return Task.CompletedTask;
  }

  public void Dispose()
  {
    _timer.Dispose();
  }
}

Questa interfaccia si compone di due metodi, StartAsync e StopAsync, che il runtime invocherà per avviare e arrestare le operazioni.

In StartAsync creiamo un oggetto timer con un delegate che conterrà la nostra logica, da ripetere - nell'esempio - ogni minuto.

Il codice è piuttosto semplice, tuttavia ci sono un paio di accorgimenti a cui dobbiamo prestare attenzione. Il primo è relativo all'esecuzione, che deve avvenire all'interno di un blocco Try...Catch. In caso contrario, un'eccezione nel task porterebbe al crash dell'intera applicazione.

Il secondo è l'uso del cancellation token, che viene impostanto nel caso in cui è stato richiesto lo shutdown dell'applicazione durante l'avvio del nostro servizio. Nel nostro esempio verifichiamo lo stato all'inizio di ogni iterazione, ma, in casi più complessi, è buona norma passarlo anche alle componenti che lo supportano, in modo da interrompere task potenzialmente lunghi, come una richiesta HTTP per esempio.

Nella fase di StopAsync, invece, ci limitiamo a impostare il timer a Infinite per disabilitarlo.

Ultima nota riguarda l'interfaccia IDisposable. Questo metodo viene tipicamente richiamato in fase di shutdown, ma è sempre buona norma invocare il Dispose delle risorse utilizzate (in questo caso il nostro oggetto Timer).

Per attivare questo componente, è sufficiente registrarlo nei servizi durante la fase di startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddHostedService<MyTimedService>();
}

In un prossimo script, vedremo come iniettare servizi nel nostro task, così da renderlo ancora più versatile.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi