0Pricing
C# Academy · Leçon

IHostedService et BackgroundService

Implémentez IHostedService et l’intergiciel abstrait BackgroundService pour exécuter des tâches en arrière-plan au sein d’un hôte .NET.

IHostedService et BackgroundService est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.

Que sont les services d’arrière-plan ?

Les services d’arrière-plan exécutent des tâches de longue durée parallèlement à votre application ASP.NET Core : traitement de files d’attente, exécution de tâches planifiées ou surveillance de ressources. .NET fournit deux abstractions : IHostedService (minimale) et la classe abstraite BackgroundService (adaptée aux boucles).

Interface IHostedService

IHostedService possède deux méthodes : StartAsync (appelée au démarrage de l’hôte) et StopAsync (appelée lors de l’arrêt progressif). Elle convient parfaitement aux tâches ponctuelles de démarrage et d’arrêt.

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 : boucles de longue durée

BackgroundService est une classe abstraite qui implémente IHostedService et fournit ExecuteAsync(CancellationToken) — une méthode qui s’exécute pendant toute la durée de vie de l’application.

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>();

Utilisation de services à portée

Les services d’arrière-plan sont des singletons. Pour utiliser des services à portée (comme DbContext), créez toujours une nouvelle portée pour chaque unité de travail à l’aide de 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);
        }
    }
}

Arrêt progressif

Lorsque l’hôte s’arrête, il déclenche le jeton d’annulation. Votre ExecuteAsync doit le surveiller et effectuer le nettoyage nécessaire. Par défaut, l’hôte attend jusqu’à 5 secondes avant de forcer l’arrêt.

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();
    }
}

File d’attente d’arrière-plan fondée sur un canal

Un modèle courant consiste à ce qu’un service d’arrière-plan lise dans une file d’attente Channel tandis que les endpoint HTTP y ajoutent des éléments de travail. Cela découple le traitement des requêtes du traitement des tâches.

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>();

Gestion des erreurs dans les services d’arrière-plan

Des exceptions non gérées dans ExecuteAsync peuvent arrêter le service silencieusement. Entourez la boucle principale d’un bloc try/catch et journalisez les erreurs afin de maintenir le service en fonctionnement malgré les échecs individuels.

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 et délai d’expiration de l’arrêt

Remplacez StopAsync pour effectuer le nettoyage. L’hôte fournit un jeton d’arrêt ; si le nettoyage prend plus de temps que le délai d’expiration, le processus est tué quoi qu’il arrive.

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));

Plusieurs services d’arrière-plan

Enregistrez plusieurs services hébergés : ils s’exécutent tous simultanément. L’hôte les démarre dans l’ordre et les arrête dans l’ordre inverse.

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 shutdown

Cas réel : service Worker de traitement des commandes

Un service d’arrière-plan qui traite les commandes en attente dans une file d’attente de base de données, avec une gestion correcte des portées et des erreurs.

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);
        }
    }
}

Vérification rapide

Pourquoi les services d’arrière-plan doivent-ils utiliser IServiceScopeFactory au lieu d’injecter directement DbContext ?

Récapitulatif : IHostedService et BackgroundService

Points essentiels :

  • IHostedService : StartAsync/StopAsync — pour les tâches de démarrage et d’arrêt
  • BackgroundService : boucle ExecuteAsync — pour les tâches d’arrière-plan continues
  • Utilisez toujours IServiceScopeFactory pour les dépendances à portée dans les services d’arrière-plan
  • Surveillez le jeton d’annulation lors de chaque await pour permettre un arrêt progressif
  • Entourez le corps de la boucle d’un bloc try/catch afin d’éviter que le service ne s’arrête en cas d’erreur
  • Utilisez HostOptions.ShutdownTimeout si le nettoyage nécessite plus de 5 secondes

Questions Fréquemment Posées

La leçon « IHostedService et BackgroundService » est-elle gratuite ?

Oui — le texte complet de « IHostedService et BackgroundService » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « IHostedService et BackgroundService » ?

Implémentez IHostedService et l’intergiciel abstrait BackgroundService pour exécuter des tâches en arrière-plan au sein d’un hôte .NET. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer C# Academy ?

Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « IHostedService et BackgroundService » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?

Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. IHostedService et BackgroundService
  2. Projets Worker Service
  3. Tâches périodiques et minuteurs
  4. Tâches planifiées avec Quartz.NET
← Retour à C# Academy