IHostedService e BackgroundService
Implementi IHostedService e la classe astratta BackgroundService per eseguire attività in background all'interno di un host .NET.
IHostedService e BackgroundService è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.
Cosa sono i servizi in background?
I servizi in background eseguono attività di lunga durata insieme all'app ASP.NET Core, ad esempio elaborando code, eseguendo attività pianificate o monitorando risorse. .NET fornisce due interfacce: IHostedService (minimale) e la classe astratta BackgroundService (adatta ai cicli).
Interfaccia IHostedService
IHostedService dispone di due metodi: StartAsync (chiamato all'avvio dell'host) e StopAsync (chiamato durante l'arresto regolare). È ideale per le attività eseguite una sola volta all'avvio o all'arresto.
public class DatabaseMigratorService : IHostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public DatabaseMigratorService(IServiceScopeFactory sf) => _scopeFactory = sf;
public async Task StartAsync(CancellationToken ct)
{
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync(ct);
}
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}
builder.Services.AddHostedService<DatabaseMigratorService>();BackgroundService: cicli di lunga durata
BackgroundService è una classe astratta che implementa IHostedService e fornisce ExecuteAsync(CancellationToken), un metodo che viene eseguito per tutta la durata dell'app.
public class HeartbeatService : BackgroundService
{
private readonly ILogger<HeartbeatService> _logger;
public HeartbeatService(ILogger<HeartbeatService> l) => _logger = l;
protected override async Task ExecuteAsync(CancellationToken ct)
{
_logger.LogInformation("Heartbeat service started");
while (!ct.IsCancellationRequested)
{
_logger.LogInformation("Heartbeat: {Time}", DateTime.UtcNow);
await Task.Delay(TimeSpan.FromSeconds(30), ct);
}
_logger.LogInformation("Heartbeat service stopping");
}
}
builder.Services.AddHostedService<HeartbeatService>();Utilizzo dei servizi Scoped
I servizi in background sono Singleton. Per utilizzare servizi Scoped (come DbContext), crei sempre un nuovo ambito per ogni unità di lavoro utilizzando IServiceScopeFactory.
public class DataSyncService : BackgroundService
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<DataSyncService> _log;
public DataSyncService(IServiceScopeFactory f, ILogger<DataSyncService> l)
{ _factory = f; _log = l; }
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
using (var scope = _factory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await SyncDataAsync(db, ct);
} // scope and DbContext disposed here
await Task.Delay(TimeSpan.FromMinutes(5), ct);
}
}
}Arresto regolare
Quando l'host viene arrestato, attiva il token di annullamento. Il metodo ExecuteAsync deve rilevarlo ed eseguire le operazioni di pulizia. Per impostazione predefinita, l'host attende fino a 5 secondi prima di forzare l'arresto.
protected override async Task ExecuteAsync(CancellationToken ct)
{
try
{
while (!ct.IsCancellationRequested)
{
await DoWorkAsync(ct);
await Task.Delay(1000, ct);
}
}
catch (OperationCanceledException)
{
// Normal shutdown — not an error
_logger.LogInformation("Service stopping due to cancellation");
}
finally
{
// Cleanup
await CleanupAsync();
}
}Coda in background basata su Channel
Un modello comune consiste nel fare in modo che il servizio in background legga da una coda Channel, mentre gli endpoint HTTP accodano gli elementi di lavoro. In questo modo la gestione delle richieste viene separata dall'elaborazione del lavoro.
public class BackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, Task>> _queue
= Channel.CreateBounded<Func<CancellationToken, Task>>(100);
public ValueTask QueueAsync(Func<CancellationToken, Task> job)
=> _queue.Writer.WriteAsync(job);
public IAsyncEnumerable<Func<CancellationToken, Task>> DequeueAllAsync(
CancellationToken ct)
=> _queue.Reader.ReadAllAsync(ct);
}
// Register and use:
builder.Services.AddSingleton<BackgroundTaskQueue>();
builder.Services.AddHostedService<QueueProcessorService>();Gestione degli errori nei servizi in background
Le eccezioni non gestite in ExecuteAsync possono arrestare il servizio senza alcun avviso. Racchiuda il ciclo principale in try/catch e registri gli errori per mantenere attivo il servizio nonostante i singoli errori.
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try
{
await ProcessNextBatchAsync(ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
break; // normal shutdown
}
catch (Exception ex)
{
_logger.LogError(ex, "Background processing error. Retrying in 5s.");
await Task.Delay(5000, ct); // backoff before retry
}
}
}StopAsync e timeout di arresto
Esegua l'override di StopAsync per effettuare le operazioni di pulizia. L'host passa un token di arresto: se la pulizia richiede più tempo del timeout, il processo viene terminato comunque.
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Service is stopping...");
// Signal internal work to stop
_internalCts.Cancel();
// Wait for the Execute loop to complete (up to timeout)
await base.StopAsync(stoppingToken);
_logger.LogInformation("Service stopped");
}
// Increase the shutdown timeout if needed:
builder.Services.Configure<HostOptions>(opt =>
opt.ShutdownTimeout = TimeSpan.FromSeconds(30));Più servizi in background
Registri più servizi ospitati: vengono eseguiti tutti contemporaneamente. L'host li avvia nell'ordine di registrazione e li arresta nell'ordine inverso.
builder.Services.AddHostedService<DatabaseMigratorService>(); // startup task
builder.Services.AddHostedService<MetricsCollectorService>(); // continuous
builder.Services.AddHostedService<EmailNotificationService>(); // continuous
builder.Services.AddHostedService<CacheWarmupService>(); // startup task
// All run concurrently after app start
// Stopped in reverse registration order on shutdownCaso reale: worker per l'elaborazione degli ordini
Un servizio in background che elabora gli ordini in sospeso da una coda nel database, con gestione corretta degli ambiti e degli errori.
public class OrderProcessorService : BackgroundService
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<OrderProcessorService> _log;
public OrderProcessorService(IServiceScopeFactory f, ILogger<OrderProcessorService> l)
{ _factory = f; _log = l; }
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try
{
using var scope = _factory.CreateScope();
var processor = scope.ServiceProvider.GetRequiredService<IOrderProcessor>();
int count = await processor.ProcessPendingAsync(ct);
_log.LogInformation("Processed {Count} orders", count);
}
catch (Exception ex) when (!ct.IsCancellationRequested)
{
_log.LogError(ex, "Order processing failed");
}
await Task.Delay(TimeSpan.FromSeconds(10), ct);
}
}
}Verifica rapida
Perché i servizi in background devono utilizzare IServiceScopeFactory invece di ricevere DbContext direttamente tramite injection?
Riepilogo: IHostedService e BackgroundService
Punti chiave:
IHostedService: StartAsync/StopAsync, per le attività di avvio e arrestoBackgroundService: ciclo ExecuteAsync, per il lavoro continuo in background- Utilizzi sempre
IServiceScopeFactoryper le dipendenze Scoped all'interno dei servizi in background - Rilevi il token di annullamento a ogni await per un arresto regolare
- Racchiuda il corpo del ciclo in try/catch per evitare che il servizio termini a causa degli errori
- Utilizzi
HostOptions.ShutdownTimeoutse la pulizia richiede più di 5 secondi
Domande Frequenti
La lezione «IHostedService e BackgroundService» è gratuita?
Sì — il testo completo di «IHostedService e BackgroundService» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.
Cosa imparerò in «IHostedService e BackgroundService»?
Implementi IHostedService e la classe astratta BackgroundService per eseguire attività in background all'interno di un host .NET. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare C# Academy?
Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «IHostedService e BackgroundService»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione C# Academy?
Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- IHostedService e BackgroundService
- Progetti Worker Service
- Attività periodiche e timer
- Processi pianificati con Quartz.NET