Nello scorso script (https://www.aspitalia.com/script/1396/Eseguire-Task-Temporizzati-Tramite-Hosted-Service-ASP.NET-Core.aspx) abbiamo visto come realizzare un hosted service in ASP.NET Core per l'esecuzione di task temporizzati.
Un oggetto di questo tipo, senza la possibilità di iniettare servizi tramite dependency injection, è ovviamente parecchio limitato. Per rimuovere questo limite, potremmo essere tentati di accettare questi servizi direttamente nel costruttore, un po' come abbiamo fatto nello script originale con l'interfaccia ILogger:
public MyTimedService(ILogger<MyTimedService> logger, IAccountService accountService, IOrdersService ordersService) { _logger = logger ?? throw new ArgumentNullException("logger"); _accountService = accountService ?? throw new ArgumentNullException("accountService"); _ordersService = ordersService ?? throw new ArgumentNullException("ordersService"); }
Questa soluzione sembra funzionare perfettamente, eppure nasconde un subdolo problema: il rilascio delle risorse.
Cosa succederebbe se, per esempio, IOrdersService utilizzasse internamente un DbContext di Entity Framework? Fintanto che lo utilizziamo in un controller, e quindi nel contesto di una richiesta, il rilascio delle risorse avverrà automaticamente al termine della richiesta stessa. Ma MyTimedService, invece, resterà attivo per l'intero ciclo di vita dell'applicazione, quindi il DbContext non verrebbe mai distrutto, con i problemi di scalabilità che possiamo facilmente immaginare.
L'approccio più corretto quindi è quello di creare un dependency injection scope per la sola durata dell'esecuzione del timer, e per farlo abbiamo bisogno di iniettare l'intero IServiceProvider:
private IServiceProvider _services; public MyTimedService(IServiceProvider services) { // non possiamo usare la constructor injection _services = services ?? throw new ArgumentNullException("services"); }
A questo punto, all'interno del timer, possiamo creare uno scope e, a sua volta, sfruttare quest'ultimo per costruire i servizi di cui abbiamo bisogno:
public Task StartAsync(CancellationToken cancellationToken) { _timer = new Timer( async state => { using (var scope = _services.CreateScope()) { var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>(); var ordersService = scope.ServiceProvider.GetRequiredService<IOrdersService>(); // ... altro codice qui ... } }, state: null, dueTime: TimeSpan.FromMinutes(1), // questo è il delay per la prima esecuzione period: TimeSpan.FromMinutes(1) ); // ogni minuto return Task.CompletedTask; }
In questo modo, la presenza dello scope all'interno del blocco using garantisce che le risorse così create verranno distrutte con la Dispose dello scope stesso, e quindi al termine di ogni iterazione.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
-
Sfruttare i tag nell'output cache di ASP.NET Core 7
-
Definire una tabella come memory optimized su Sql Server tramite EF Core
-
Aggiungere le issue di più repository in una board in GitHub
-
Raggruppare i parametri di una minimal API in un singolo oggetto in ASP.NET Core
-
Utilizzare parametri a livello di controller nel routing di ASP.NET Core
-
Gestire tipi complessi in query string grazie a IParsable in ASP.NET Core 7.0
-
Sfruttare l'output cache di ASP.NET Core 7 con i controller
-
Eliminare spazio inutilizzato in un Azure Container Registry
-
Montare blob e file share su Azure App Service
-
Ottenere il riferimento alla finestra che ha aperto un'altra finestra con HTML5 e JavaScript
-
Utilizzare il Service Bus Explorer integrato del portale Azure
-
Q# for Quantum Programming, an "only for the brave" session
I più letti di oggi
- Ricevere avvisi su metriche dei server Azure Arc
- Workflow di continuous deployment tramite pull request label in GitHub
- Effettuare update massivi con Entity Framework Core 7
- Sottoscrizione agli eventi sul contenitore in JavaScript
- Effettuare l'upload di un file via FTP con la libreria FluentFTP di .NET
- Catturare la telemetria degli eventi di output cache in ASP.NET Core
- .NET Conference Italia 2022 - Milano e Online
- Monitorare i server on-premises con Azure Arc