Negli script precedenti abbiamo introdotto alcuni concetti utili tutte le volte che vogliamo gestire dei task in background in ASP.NET Core: abbiamo visto i fondamenti per creare un Hosted Service (https://www.aspitalia.com/script/1396/Eseguire-Task-Temporizzati-Tramite-Hosted-Service-ASP.NET-Core.aspx), come iniettarvi correttamente servizi (https://www.aspitalia.com/script/1397/Iniettare-Servizi-Hosted-Service-ASP.NET-Core.aspx) e anche il modo più indicato per effettuarne lo shutdown (https://www.aspitalia.com/script/1398/Graceful-Shutdown-Hosted-Service-ASP.NET-Core.aspx).
Tutti questi esempi hanno in comune il medesimo limite: il servizio si avvia allo startup dell'applicazione, e termina solo al momento del suo shutdown. Spesso, invece, abbiamo bisogno di un controllo più preciso, per esempio potremmo voler temporaneamente arrestarne l'esecuzione.
Immaginiamo per esempio di avere timer che effettui il polling sul database, per controllare la modifica di un dato. In presenza di un'operazione bulk, che modifichi un migliaia di righe, magari può convenire interrompere il polling, che altrimenti finirebbe per segnalare un gran numero di notifiche consecutive, e riattivarlo solo al termine dell'operazione stessa.
Ciò che dobbiamo fare è innanzitutto recuperare una reference all'istanza dell'hosted service desiderato, per esempio tramite constructor injection:
private TimerService _timerService; public TimerController(IEnumerable<IHostedService> services) { _timerService = services.OfType<TimerService>().Single(); }
Per design, ASP.NET Core ammette un solo hosted service per tipo, quindi è corretto utilizzare la clausola Single una volta che abbiamo filtrato per il tipo di servizio desiderato.
A questo punto, possiamo invocarne i metodi StartAsync e StopAsync secondo le nostre necessità, per esempio come risposta a una chiamata HTTP POST:
[HttpPost] public async Task<IActionResult> SetStateAsync([FromBody] string newState) { switch (newState) { case "start": await _timerService.StartAsync(new CancellationToken()); break; case "stop": await _timerService.StopAsync(new CancellationToken()); break; default: return this.BadRequest(); } return this.Accepted(); }
Ovviamente, anche il codice del nostro hosted service dovrà essere scritto in modo da supportare correttamente Start e Stop multipli. Per esempio, la nostra versione originale non è adeguata, perché istanzia continuamente un nuovo oggetto Timer, con il risultato che chiamate multiple al metodo Start genererebbero diversi timer contemporaneamente in esecuzione:
public Task StartAsync(CancellationToken cancellationToken) { _timer = new Timer(...); return Task.CompletedTask; }
Una versione più corretta invece, deve verificare che il timer sia già istanziato, e in questo caso, limitarsi semplicemente a riattivarlo:
public class TimerService : IHostedService { private Timer _timer; private CancellationTokenSource _tokenSource; public Task StartAsync(CancellationToken cancellationToken) { _tokenSource = new CancellationTokenSource(); if (_timer == null) { _timer = new Timer(async s => { Console.WriteLine("timer trigger"); // istanziamo inizialmente come "spento" così da // evitare una prima doppia esecuzione }, null, Timeout.Infinite, Timeout.Infinite); } // qui attiviamo il timer _timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(10)); Console.WriteLine("timer started"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { _timer.Change(Timeout.Infinite, Timeout.Infinite); _tokenSource.Cancel(); Console.WriteLine("timer stopped"); return Task.CompletedTask; } }
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
-
Implementare l'infinite scroll con QuickGrid in Blazor Server
-
Specificare il versioning nel path degli URL in ASP.NET Web API
-
Eseguire un metodo asincrono dopo il set di una proprietà in Blazor 8
-
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
-
Utilizzare HiLo per ottimizzare le insert in un database con Entity Framework
-
Load test di ASP.NET Core con k6
-
Effettuare lo stream della risposta in ASP.NET Core tramite IAsyncEnumerable
-
Usare Refit e Polly in Blazor per creare client affidabili e fortemente tipizzati
-
Potenziare Azure AI Search con la ricerca vettoriale
-
Routing statico e PreRendering in una Blazor Web App
-
Trasformare qualsiasi backend in un servizio GraphQL con Azure API Management
-
Come migrare da una form non tipizzata a una form tipizzata in Angular