Operazioni in background con gli Hosted Service di ASP.NET Core

di Moreno Gentili, in ASP.NET Core,

In precedenti script (https://www.aspitalia.com/script/1190/Utilizzare-Hangfire-Schedulare-Job-ASP.NET-MVC.aspx) abbiamo affrontato l'importanza di eseguire task (o "job") in background in applicazioni ASP.NET "tradizionali".

Lavorare in background continua ad essere importante anche nelle applicazioni ASP.NET Core ed è ideale sia per compiere operazioni ricorrenti come inviare periodicamente della reportistica e sia per eseguire elaborazioni di lunga durata richieste dagli utenti.
Per eseguire operazioni in background, possiamo sfruttare gli Hosted Service, una funzionalità integrata in ASP.NET Core che offre un'alternativa a soluzioni di terze parti come Hangfire.

Il primo Hosted Service

Per creare un Hosted Service è sufficiente scrivere una propria classe che implementi l'interfaccia IHostedService, come nell'esempio seguente. Inseriamo questo codice in un nuovo file di codice all'interno di una sottodirectory qualsiasi, come /HostedServices.

public class MyHostedService : IHostedService
{
  public async Task StartAsync(CancellationToken cancellationToken)
  {
    //Avvio il lavoro in background
  }
  public async Task StopAsync(CancellationToken cancellationToken)
  {
    //Arresto il lavoro in background
  }
}

I due metodi StartAsync e StopAsync ci forniscono un controllo basilare per iniziare e terminare il lavoro in background in concomitanza con l'avvio e l'arresto dell'applicazione ASP.NET Core. In caso di operazioni ricorrenti, sarà una nostra responsabilità quella di preparare un timer per avviare il lavoro a orari o intervalli ben determinati.

Avviare e arrestare il lavoro in background


Il metodo StartAsync ha l'unico scopo di avviare un task in background. Il web host invoca tale metodo solo dopo che l'applicazione è pronta a ricevere richieste HTTP. In questo modo, la presenza di uno o più Hosted Service non impatta sui tempi di avvio e ogni richiesta HTTP può giungere ai middleware anche se gli Hosted Service non sono ancora stati avviati.


Il metodo StopAsync viene invece invocato all'arresto dell'applicazione. Per default, il web host concede al servizio 5 secondi per concludere il lavoro, prima che sia terminato forzatamente. Possiamo modificare tale intervallo con il metodo UseShutdownTimeout durante la configurazione del web host nel file Program.cs.
In questo esempio impostiamo l'attesa a 20 secondi.

public static IWebHost BuildWebHost(string[] args) =>
  WebHost.CreateDefaultBuilder(args)
    .UseShutdownTimeout(TimeSpan.FromSeconds(20))
    .UseStartup<Startup>()
    .Build();


A prescindere da quale sia il tempo di attesa configurato, ai metodi StartAsync e StopAsync viene fornito un oggetto di tipo CancellationToken da passare come argomento alle nostre operazioni asincrone affinché non si protraggano oltre il tempo limite consentito.

Registrare l'Hosted Service come servizio di ASP.NET Core

Infine, non resta che registrare l'Hosted Service dalla classe Startup, così come registreremmo un qualsiasi altro servizio. La strategia da usare per il ciclo di vita è Singleton, dato che il servizio viene avviato e arrestato con l'applicazione.

public void ConfigureServices(IServiceCollection services)
{
  services.AddSingleton<IHostedService, MyHostedService>();
  //Qui registriamo altri eventuali Hosted Service
  //services.AddSingleton<IHostedService, AnotherHostedService>();
}

Gli Hosted Service sono parte integrante di un'applicazione ASP.NET Core e, proprio come gli altri servizi, possono avvalersi del meccanismo di dependency injection o essere iniettati essi stessi come dipendenze in altri servizi o Controller.

Segue l'esempio di un Hosted Service che usa il servizio ILogger di ASP.NET Core per tracciare la sua attività.

public class MyHostedService : IHostedService
{
  private readonly ILogger<MyHostedService> logger;
  
  public MyHostedService(ILogger<MyHostedService> logger) {
    this.logger = logger;
  }
  public async Task StartAsync(CancellationToken cancellationToken)
  {
  logger.LogInformation("Avvio il lavoro in background");
  }
  public async Task StopAsync(CancellationToken cancellationToken)
  {
  logger.LogInformation("Arresto il lavoro in background");
  }
}

Uno scenario più concreto


Uno dei vantaggi di eseguire un'operazione di lunga durata in background è quello di offrire una migliore esperienza d'uso all'utente, che così non è costretto ad attenderne il completamento.

Ad esempio, pensiamo a una pagina web che permetta all'utente di inviare un'e-mail, proprio come il form di contatto che abbiamo realizzato in un precedente script (https://www.aspitalia.com/script/1250/Form-Contatti-ASP.NET-Core-MVC.aspx).

Nel caso in cui il server SMTP dovesse risultare lento o irraggiungibile, la nostra applicazione ne subirebbe gli effetti e apparirebbe come poco responsiva o malfunzionante. Inviando l'e-mail in background, abbiamo tutta la libertà di comunicare all'utente che la sua richiesta è stata presa in carico per poi affrontare l'invio vero e proprio in maniera asincrona.

Un'applicazione dimostrativa è disponibile a questo indirizzo: https://github.com/BrightSoul/background-email-sender

L'Hosted Service, oltre ad eseguire del lavoro in background, può anche offrire un'interfaccia pubblica verso altri servizi affinché siano in grado di accodare ulteriore lavoro o controllare il progresso corrente.

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