Come usare BackgroundService in ASP.NET Core per task periodici
🔍 Introduzione: perché servono i task periodici?
Nel ciclo di vita di un’applicazione server ci sono molti scenari in cui serve eseguire operazioni ricorrenti o in background, ad esempio:
✅ Pulizia di dati temporanei o vecchi
✅ Invio di email pianificate o promemoria
✅ Sincronizzazione con sistemi esterni (ERP, CRM, API)
✅ Monitoraggio di code o eventi
✅ Aggiornamento di cache o indici
Molti sviluppatori iniziano con Timer
, Thread
, Task.Run
, ma in ambienti ASP.NET Core (soprattutto su cloud) queste soluzioni sono fragili e non gestiscono bene:
avvio insieme all’applicazione
riavvii o aggiornamenti controllati
graceful shutdown
dependency injection
Per risolvere questi problemi ASP.NET Core offre IHostedService e la sua implementazione astratta più comoda: BackgroundService.
🧭 Cosa sono IHostedService e BackgroundService?
ASP.NET Core è progettato come un generic host che può gestire più servizi.IHostedService
è un'interfaccia per definire componenti di lunga durata:
Viene avviato all’avvio dell’app
Può eseguire un ciclo infinito o task singoli
Gestisce in modo automatico il lifecycle (avvio e stop)
BackgroundService
è una classe base fornita da Microsoft che implementa IHostedService
semplificando il lavoro.
Tu devi solo overridare ExecuteAsync
.
✅ Caratteristiche chiave di BackgroundService
⭐️ Avvio automatico all'avvio dell'app
⭐️ Esecuzione asincrona e cancellabile
⭐️ Integrato con il DI container
⭐️ Graceful shutdown con CancellationToken
⭐️ Logging integrato
🧪 Scenario reale: pulizia di dati obsoleti
Immagina di avere una tabella TemporaryFiles
che va pulita ogni ora per eliminare record più vecchi di 24 ore.
Vogliamo creare un servizio periodico che:
si avvia con l’app
gira finché il server è online
ogni ora pulisce i dati vecchi
🛠️ Passo 1 – Creare il servizio
Crea una classe che estende BackgroundService
:
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; public class CleanupService : BackgroundService { private readonly ILogger<CleanupService> _logger; private readonly ITemporaryFileRepository _repository; public CleanupService( ILogger<CleanupService> logger, ITemporaryFileRepository repository) { _logger = logger; _repository = repository; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("CleanupService avviato."); while (!stoppingToken.IsCancellationRequested) { try { _logger.LogInformation("Eseguo pulizia dei dati alle {time}", DateTimeOffset.Now); await _repository.DeleteOldFilesAsync(stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "Errore durante la pulizia"); } // Aspetto un'ora prima del prossimo ciclo await Task.Delay(TimeSpan.FromHours(1), stoppingToken); } _logger.LogInformation("CleanupService in arresto."); } }
📌 Cosa succede qui?
✅ ExecuteAsync
è il loop infinito controllato
✅ Rispettiamo il CancellationToken
per shutdown ordinato
✅ Logging integrato per osservabilità
✅ Retry naturale a ogni intervallo
🛠️ Passo 2 – Registrazione nel DI container
Nel Program.cs
di ASP.NET Core:
builder.Services.AddHostedService<CleanupService>(); builder.Services.AddScoped<ITemporaryFileRepository, TemporaryFileRepository>();
✅ AddHostedService
dice al sistema di avviare il servizio all’avvio.
✅ Puoi usare tutti i servizi del tuo container!
🧩 Implementazione del repository (esempio semplificato)
public class TemporaryFileRepository : ITemporaryFileRepository { private readonly AppDbContext _db; public TemporaryFileRepository(AppDbContext db) { _db = db; } public async Task DeleteOldFilesAsync(CancellationToken cancellationToken) { var threshold = DateTime.UtcNow.AddHours(-24); var oldFiles = await _db.TemporaryFiles .Where(f => f.CreatedAt < threshold) .ToListAsync(cancellationToken); _db.TemporaryFiles.RemoveRange(oldFiles); await _db.SaveChangesAsync(cancellationToken); } }
✅ Supporto asincrono
✅ Usa Entity Framework e il CancellationToken
✅ Rispetta l’integrità transazionale
📈 Vantaggi di questa architettura
✅ Start & stop gestiti dal framework
✅ Integrato con la Dependency Injection
✅ Resiliente agli errori (try/catch nel loop)
✅ Scalabile su più ambienti (Docker, Azure App Service, K8s)
✅ Supporto nativo a graceful shutdown
⚡ Best Practice
✅ Rispetta sempre il CancellationToken
✅ Evita blocchi sincroni (Thread.Sleep
)
✅ Usa logging dettagliato per il monitoring
✅ Evita lavori pesanti inline → usa queue o task separati
✅ Gestisci errori con retry (Polly, custom backoff)
✅ Monitora l’health check del servizio
🧭 Esempi di Use Case reali
Pulizia di sessioni scadute
Invio batch di email o notifiche push
Sincronizzazione con sistemi esterni
Aggiornamento di cache distribuite
Polling di code RabbitMQ/Azure Queue
Generazione periodica di report
🔥 Alternative per scenari più complessi
Hangfire per job persistenti e dashboard
Azure Functions Timer Trigger
AWS Lambda con EventBridge cron
Worker Service standalone in .NET
✅ Conclusione
BackgroundService
è lo standard in ASP.NET Core per operazioni periodiche e di lunga durata.
✔️ Semplice da implementare
✔️ Integrato con DI e logging
✔️ Gestione lifecycle professionale
✔️ Scalabile in cloud e container
Se devi schedulare task periodici in modo sicuro e mantenibile, questa è la strada giusta.