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 alias per tipi generici e tuple in C#
-
Criptare la comunicazione con mTLS in Azure Container Apps
-
Gestire liste di tipi semplici con Entity Framework Core
-
Creare una libreria CSS universale: Clip-path
-
Proteggere le risorse Azure con private link e private endpoints
-
Utilizzare i primary constructor di C# per inizializzare le proprietà
-
Ottimizzare la latenza in Blazor 8 tramite InteractiveAuto render mode
-
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
-
Potenziare Azure AI Search con la ricerca vettoriale
-
Usare una container image come runner di GitHub Actions
-
Creazione di componenti personalizzati in React.js con Tailwind CSS
-
Migliorare i tempi di risposta di GPT tramite lo streaming endpoint in ASP.NET Core