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
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
-
Creare un webhook in Azure DevOps
-
Migliorare l'organizzazione delle risorse con Azure Policy
-
Migliorare la scalabilità delle Azure Function con il Flex Consumption
-
Inference di dati strutturati da testo con Semantic Kernel e ASP.NET Core Web API
-
Eseguire operazioni sui blob con Azure Storage Actions
-
Ordine e importanza per @layer in CSS
-
Limitare le richieste lato server con l'interactive routing di Blazor 8
-
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
-
Eseguire un metodo asincrono dopo il set di una proprietà in Blazor 8
-
Miglioramenti agli screen reader e al contrasto in Angular
-
Utilizzare una qualunque lista per i parametri di tipo params in C#
-
Utilizzare la funzione EF.Parameter per forzare la parametrizzazione di una costante con Entity Framework