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
-
Creare un webhook in Azure DevOps
-
Creare una custom property in GitHub
-
Sfruttare al massimo i topic space di Event Grid MQTT
-
Disabilitare automaticamente un workflow di GitHub
-
Come EF 8 ha ottimizzato le query che usano il metodo Contains
-
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
-
Creare una libreria CSS universale: Immagini
-
Eseguire query manipolando liste di tipi semplici con Entity Framework Core
-
Creazione di componenti personalizzati in React.js con Tailwind CSS
-
Usare il colore CSS per migliorare lo stile della pagina
-
Utilizzare il trigger SQL con le Azure Function
-
Effettuare il log delle chiamate a function di GPT in ASP.NET Web API