Qu'est-ce que le .NET Generic Host ? - Le socle de la DI, de la configuration et des logs

· Mis à jour le: · · C#, .NET, Generic Host, Worker, Conception

Quand on commence à écrire une application console ou un worker en .NET, au début, il suffit d’écrire un peu de code dans Main. Mais dès que le projet grandit un peu, ce sont généralement les besoins suivants qui s’accumulent.

  • Vous voulez lire appsettings.json
  • Vous voulez pouvoir surcharger les valeurs avec des variables d’environnement
  • Vous voulez journaliser avec ILogger
  • Vous ne voulez pas que la création des services soit une suite ininterrompue de new
  • Vous voulez faire tourner une boucle en arrière-plan
  • Vous voulez pouvoir vous arrêter proprement sur Ctrl+C ou à l’arrêt d’un service

C’est là qu’intervient le Generic Host. Mais ce nom lui-même prête un peu à confusion.

  • Quelle est la différence entre Host.CreateApplicationBuilder et Host.CreateDefaultBuilder ?
  • IHost est-il la même chose qu’un conteneur DI ?
  • Quel est son lien avec BackgroundService ?
  • Est-il différent du WebApplicationBuilder d’ASP.NET Core ?
  • Vaut-il la peine d’être utilisé même dans une application console ?

Quand ces points se mélangent, le Generic Host peut sembler « réservé aux applications Web », ou à l’inverse « quelque chose qu’il faudrait systématiquement héberger dans un host ». Ces deux visions sont un peu approximatives.

Dans cet article, en nous plaçant surtout dans la pratique actuelle de .NET 6 et versions ultérieures, nous clarifions d’abord ces quatre points.

  • Ce qu’est réellement le Generic Host
  • Ce qu’il prend en charge, tout regroupé
  • La relation entre Host.CreateApplicationBuilder / Host.CreateDefaultBuilder / WebApplication.CreateBuilder
  • Par où entrer en douceur

Table des matières

  1. La conclusion d’abord (en une phrase)
  2. Les tableaux à consulter en premier
    • 2.1. Ce que contient le Generic Host
    • 2.2. Les différences entre les builders
  3. La vue d’ensemble du Generic Host (schéma)
  4. Ce que le Generic Host apporte
    • 4.1. Regrouper la logique de démarrage en un seul endroit
    • 4.2. DI, configuration et logs connectés dès le départ
    • 4.3. Un arrêt propre et un fonctionnement résident faciles à gérer
  5. Configuration minimale
    • 5.1. Exemple minimal dans une application console
    • 5.2. appsettings.json
    • 5.3. Ajouter un BackgroundService
  6. Schémas types
    • 6.1. Outils console de courte durée
    • 6.2. Workers / services en arrière-plan
    • 6.3. Également présent sous ASP.NET Core
  7. Cas où il est adapté
  8. Cas où il ne convient pas / est excessif
  9. Pièges courants
  10. Résumé
  11. Références

1. La conclusion d’abord (en une phrase)

  • Le Generic Host est le socle qui prend en charge, en un seul endroit, le démarrage et la durée de vie d’une application .NET.
  • Il englobe la DI, la configuration, les logs, IHostedService / BackgroundService, ainsi que la gestion de l’arrêt de l’application.
  • Pour une nouvelle application non Web, il est naturel de commencer par Host.CreateApplicationBuilder(args).
  • Le WebApplicationBuilder d’ASP.NET Core n’est pas non plus un monde à part : c’est le même concept de host, élargi pour offrir un point d’entrée dédié au Web.
  • Autrement dit, le Generic Host ne se résume pas à un conteneur DI : c’est le mécanisme qui rassemble le point d’assemblage de l’application et la gestion de son cycle de vie.

En résumé, dès que l’application dépasse un peu le stade « lire les arguments, afficher une fois, puis se terminer », le Generic Host devient nettement rentable. À l’inverse, il n’est pas nécessaire de l’embarquer systématiquement dans un petit outil qui n’a pas encore atteint ce stade.

2. Les tableaux à consulter en premier

2.1. Ce que contient le Generic Host

Commencer par distinguer le contenu de cette boîte simplifie beaucoup les choses par la suite.

Élément Ce dont le Generic Host s’occupe Pourquoi c’est utile
DI Construit les services à partir d’IServiceCollection Facilite la réduction des chaînes de new
Configuration Rassemble appsettings.json, les variables d’environnement, les arguments de ligne de commande, etc. Facilite la gestion des différences entre environnements
Logging Met en place le socle nécessaire pour utiliser ILogger<T> Facilite le remplacement ultérieur de la destination des logs
Hosted service Gère le démarrage et l’arrêt d’IHostedService / BackgroundService Facilite la séparation du traitement résident et du corps de l’application
Lifetime Gère le démarrage et l’arrêt via IHostApplicationLifetime, IHostEnvironment, etc. Facilite l’uniformisation de la façon de s’arrêter sur Ctrl+C, SIGTERM ou l’arrêt d’un service

Ce qui compte ici, c’est que le Generic Host n’est pas « un simple wrapper DI pratique ». En réalité, la façon de voir les choses la plus fiable est de le considérer comme une boîte qui câble d’un seul coup tout ce qui entoure le point d’entrée de l’application.

2.2. Les différences entre les builders

Là aussi, il est plus rapide de tout voir sur un seul tableau, dès le départ.

Point d’entrée Usage principal Style d’écriture Premier choix
Host.CreateApplicationBuilder(args) Nouvelles applications non Web (console / worker, etc.) Écrire directement dans builder.Services / builder.Configuration / builder.Logging À privilégier pour du neuf
Host.CreateDefaultBuilder(args) Code existant ou configuration reposant surtout sur les anciennes méthodes d’extension Enchaîner des méthodes comme ConfigureServices À privilégier si vous avez des actifs existants
WebApplication.CreateBuilder(args) Applications Web / API ASP.NET Core Le Generic Host auquel s’ajoutent les besoins spécifiques au Web À privilégier pour le Web

CreateApplicationBuilder et CreateDefaultBuilder ne forment pas un couple où l’un serait une nouvelle fonctionnalité et l’autre une chose complètement différente.

Les deux offrent les mêmes fonctionnalités de base et le même comportement par défaut. Ce qui diffère, c’est surtout le style d’écriture.

Pour une nouvelle application non Web, il est aujourd’hui naturel de partir de Host.CreateApplicationBuilder(args). Il est utile de considérer WebApplication.CreateBuilder(args) comme ce même flux, élargi en un point d’entrée dédié au Web.

3. La vue d’ensemble du Generic Host (schéma)

Voici, esquissée à grands traits, la vue d’ensemble.

args / variables d'environnement / appsettings.jsonHost.CreateApplicationBuilder(args)builder.Configurationbuilder.Servicesbuilder.LoggingIHostedService / BackgroundServicebuilder.Build()IHostRun / RunAsyncDémarrage / arrêt / Ctrl+C / SIGTERM

En général, on crée le builder dans Program.cs, on ajoute des services à builder.Services, on ajuste builder.Configuration et builder.Logging selon les besoins, puis on appelle enfin Build() pour obtenir un IHost, que l’on exécute avec Run() / RunAsync().

Ce qui est discret mais considérable, c’est tout ce qui est déjà en place dès l’appel à Host.CreateApplicationBuilder(args). Par défaut, on retrouve par exemple les éléments suivants.

  • La racine de contenu (content root) est le répertoire courant
  • La configuration du host provient des variables d’environnement préfixées par DOTNET_ et des arguments de ligne de commande
  • La configuration de l’application provient de appsettings.json, appsettings.{Environment}.json, des user secrets en environnement Development, des variables d’environnement et des arguments de ligne de commande
  • Les logs sont envoyés vers Console / Debug / EventSource / EventLog (Windows uniquement)
  • En environnement Development, la validation des scopes et la validation des dépendances sont activées

Autrement dit, on ne câble pas tout depuis zéro sans y réfléchir : dès le départ, un socle « largement suffisant pour un usage courant » est déjà en place.

4. Ce que le Generic Host apporte

4.1. Regrouper la logique de démarrage en un seul endroit

L’effet le plus discret mais le plus important du Generic Host est que le point d’entrée de l’application devient difficile à disperser.

Dès que l’application grandit un peu, voici ce qui a tendance à s’accumuler autour de Main.

  • Le chargement des fichiers de configuration
  • Le remplacement des valeurs selon l’environnement
  • L’initialisation du logger
  • L’assemblage de HttpClient, des repositories et des services
  • Le démarrage du traitement en arrière-plan
  • Le nettoyage lors des signaux de fin

Si l’on relie tout cela à la main, sans host, même léger au début, le point d’entrée finit peu à peu par devenir poisseux.

Avec le Generic Host, Program.cs devient clairement « l’endroit où l’on assemble toutes les dépendances en un bloc ». Cette seule clarification change considérablement la facilité de relecture du code.

4.2. DI, configuration et logs connectés dès le départ

Avec le Generic Host, la DI, la configuration et les logs reposent dès le départ sur le même socle.

Du côté des classes, par exemple, on peut recevoir naturellement des éléments comme ceux-ci.

  • ILogger<T>
  • IConfiguration
  • IHostEnvironment
  • IOptions<T>

Ce qui paie ici, c’est que la façon de lire la configuration et la façon de construire les services ont peu de chances de suivre des styles différents.

S’il n’y a qu’un ou deux paramètres, lire directement IConfiguration["Section:Key"] suffit à faire fonctionner les choses. Mais quand, en pratique, les paramètres se multiplient, il est plus paisible de regrouper chaque section dans une classe via IOptions<T>.

De la même façon, pour les logs, plutôt que de fabriquer soi-même un ILoggerFactory un peu partout, injecter ILogger<T> dans les classes qui en ont besoin rend les choses bien plus lisibles.

Ce qui rend le Generic Host pratique, c’est qu’il ne traite pas ces sujets séparément : il les prend en charge ensemble, comme le socle de l’application dans son ensemble.

4.3. Un arrêt propre et un fonctionnement résident faciles à gérer

Le Generic Host ne s’occupe pas seulement de « comment démarrer », mais aussi de « comment s’arrêter ».

Quand le host démarre, StartAsync est appelé sur chaque IHostedService enregistré. Dans les services worker, ExecuteAsync s’exécute sur les hosted services, y compris BackgroundService.

Ici, « arrêt propre » ne signifie pas couper le traitement d’un coup, mais terminer dans cet ordre :

  • diffuser le signal d’arrêt
  • sortir des boucles et des attentes
  • nettoyer les connexions et les ressources

Pour les applications qui tournent longtemps, ce point compte énormément. Des événements comme Ctrl+C, SIGTERM ou l’arrêt d’un service permettent d’uniformiser facilement la façon dont l’application entière s’arrête.

De plus, quand l’application elle-même veut demander son arrêt, IHostApplicationLifetime.StopApplication() est disponible. On peut ainsi émettre, dans le contexte du host, le signal « le travail est terminé, merci de vous arrêter proprement ».

5. Configuration minimale

5.1. Exemple minimal dans une application console

Le premier point important : utiliser le Generic Host ne signifie pas qu’il faille obligatoirement créer un BackgroundService.

Même pour un outil console qui ne s’exécute qu’une seule fois, le Generic Host est parfaitement utilisable dès lors que l’on veut la DI, la configuration et les logs.

Pour l’ajouter après coup à un projet console classique, on référence d’abord Microsoft.Extensions.Hosting.

dotnet add package Microsoft.Extensions.Hosting

Un exemple minimal de Program.cs ressemble à ceci.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<JobRunner>();

using IHost host = builder.Build();

try
{
    JobRunner runner = host.Services.GetRequiredService<JobRunner>();
    await runner.RunAsync();
    return 0;
}
catch (Exception ex)
{
    ILogger logger = host.Services
        .GetRequiredService<ILoggerFactory>()
        .CreateLogger("Program");

    logger.LogError(ex, "Unhandled exception occurred during job execution.");
    return 1;
}

internal sealed class JobRunner(
    ILogger<JobRunner> logger,
    IConfiguration configuration,
    IHostEnvironment hostEnvironment)
{
    public Task RunAsync()
    {
        string message = configuration["Sample:Message"] ?? "(no message)";

        logger.LogInformation("Environment: {EnvironmentName}", hostEnvironment.EnvironmentName);
        logger.LogInformation("Message: {Message}", message);

        return Task.CompletedTask;
    }
}

Si l’application ne reste pas résidente longtemps, il n’est pas nécessaire d’aller jusqu’à RunAsync(). Il suffit d’appeler Build(), de résoudre les services nécessaires, d’effectuer le travail, puis de se terminer. On profite tout de même largement des avantages du Generic Host.

Ce point est étonnamment important. Il n’est pas nécessaire d’apporter systématiquement le modèle Worker, même pour des jobs de courte durée.

5.2. appsettings.json

Pour l’exemple ci-dessus, un fichier de configuration aussi minimal que celui-ci suffit.

{
  "Sample": {
    "Message": "hello from Generic Host"
  }
}

Dans cet exemple, on lit directement configuration["Sample:Message"]. Si l’on ne consulte qu’une ou deux valeurs, cela suffit amplement.

Mais quand, en pratique, les paramètres se multiplient, il est préférable de s’orienter vers une approche qui consiste à

  • répartir chaque section dans sa propre classe
  • l’injecter via IOptions<T>
  • la valider au démarrage

ce qui évite plus facilement de disperser des chaînes de clés un peu partout.

De plus, avec les valeurs par défaut du Generic Host, ce n’est pas seulement appsettings.json qui est pris en compte : appsettings.{Environment}.json, les variables d’environnement et les arguments de ligne de commande sont également connectés, ce qui permet assez naturellement de « ne remplacer les valeurs qu’en développement » ou de « surcharger avec des variables d’environnement en production ».

5.3. Ajouter un BackgroundService

Pour un traitement de longue durée, utiliser BackgroundService est l’approche la plus naturelle.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddScoped<PollingJob>();
builder.Services.AddHostedService<PollingWorker>();

using IHost host = builder.Build();
await host.RunAsync();

internal sealed class PollingWorker(
    IServiceScopeFactory scopeFactory,
    ILogger<PollingWorker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));

        while (await timer.WaitForNextTickAsync(stoppingToken))
        {
            using IServiceScope scope = scopeFactory.CreateScope();
            PollingJob job = scope.ServiceProvider.GetRequiredService<PollingJob>();

            await job.RunAsync(stoppingToken);
            logger.LogInformation("Polling completed.");
        }
    }
}

internal sealed class PollingJob(ILogger<PollingJob> logger)
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation("Do work here.");
        return Task.CompletedTask;
    }
}

Il y a deux points à retenir dans cet exemple.

  1. Le corps d’un BackgroundService est ExecuteAsync
  2. Si l’on a besoin de dépendances scoped, on crée un scope avec IServiceScopeFactory

Un BackgroundService n’a lui-même aucun scope par défaut. Si l’on veut utiliser un service scoped comme DbContext, résoudre le job à l’intérieur d’un scope, comme ci-dessus, est la méthode sûre.

Le choix de l’outil d’exécution périodique lui-même est un autre sujet, mais si l’on écrit dans une logique async, PeriodicTimer reste assez paisible. Ce point rejoint d’ailleurs notre article associé sur les timers.

6. Schémas types

6.1. Outils console de courte durée

Pour les applications qui font leur travail une seule fois puis se terminent — batchs, outils de conversion, commandes de maintenance — le Generic Host est tout à fait utilisable.

Il convient bien à des scénarios comme ceux-ci.

  • Vous voulez lire des fichiers de configuration
  • Vous voulez émettre des logs
  • Vous voulez injecter HttpClient ou des repositories
  • Vous voulez renvoyer un code de sortie

Pour ce type d’application, se précipiter sur BackgroundService et RunAsync() est un peu lourd et sollicite excessivement la gestion du cycle de vie du host.

Pour un job de courte durée, il suffit de résoudre et d’exécuter un JobRunner, comme dans l’exemple minimal précédent.

6.2. Workers / services en arrière-plan

Pour des traitements comme les workers résidents, le polling, la consommation de files, la surveillance ou l’exécution périodique, la combinaison du Generic Host et de BackgroundService est très naturelle.

Voici en particulier ce qui est appréciable.

  • Le déroulement du démarrage et de l’arrêt est uniformisé côté host
  • Les logs, la configuration et la DI sont utilisables dès le départ
  • L’annulation se propage facilement sur Ctrl+C ou un signal d’arrêt
  • Le corps du traitement résident est facile à séparer de Program.cs

De plus, cela se relie facilement aux contextes des Windows Service ou des conteneurs. Si l’application est destinée à devenir une application résidente, le Generic Host en est un socle très naturel.

Lors de la transformation en Windows Service, plutôt que de rechercher des fichiers en supposant le répertoire courant, il vaut mieux raisonner à partir de IHostEnvironment.ContentRootPath, ce qui limite les incidents — car c’est dans le contexte du host que le « chemin de référence de l’application » est déterminé.

6.3. Également présent sous ASP.NET Core

Les applications Web / API utilisent WebApplication.CreateBuilder(args), ce qui peut, à première vue, sembler un monde à part par rapport au Generic Host.

Mais, sur le fond, les deux sont très liés.

  • builder.Services
  • builder.Configuration
  • builder.Logging

C’est justement pour cette raison que le style d’écriture se ressemble.

Dans ASP.NET Core, le démarrage du serveur HTTP lui-même fait partie du lifetime du host. Autrement dit, comprendre le Generic Host aide aussi, en ce sens, à saisir pourquoi, en lisant le Program.cs côté Web, on manipule ici la DI, la configuration ou les logs.

7. Cas où il est adapté

Voici les situations où le Generic Host a tendance à s’intégrer avec bonheur.

  • Applications console utilisant la configuration, les logs et la DI
  • Workers de type consommateur de file, poller, watchdog ou scheduler
  • Applications de longue durée nécessitant un nettoyage sur Ctrl+C ou SIGTERM
  • Applications susceptibles d’évoluer vers un Windows Service ou un conteneur résident
  • Applications que l’on souhaite aligner sur les mêmes idiomes de méthodes d’extension qu’ASP.NET Core

Ce qu’elles ont en commun, c’est de ne pas vouloir traiter à la légère le point d’entrée de l’application et la gestion de son cycle de vie.

8. Cas où il ne convient pas / est excessif

À l’inverse, il existe des situations où le Generic Host n’a pas besoin d’être mis au premier plan dès le départ.

  • Un petit outil qui se contente de lire des arguments une fois et d’afficher un résultat une fois avant de se terminer
  • Du code de vérification rapide utilisé seulement quelques dizaines de minutes
  • Les projets de bibliothèque
  • Les cas où l’on ne lit qu’un seul paramètre et où la DI, les logs ou la gestion du cycle de vie ne sont pas nécessaires

Dans ces cas, une implémentation plus simple qu’un host complet est plus tranquille.

Ce qui compte, c’est que ce n’est pas parce que le Generic Host est puissant qu’il est obligatoire pour tous les exécutables.

9. Pièges courants

Pour finir, voici les pièges dans lesquels on tombe facilement lors d’une première approche du Generic Host.

  • Voir le Generic Host uniquement comme un conteneur DI
    • En réalité, c’est un socle qui inclut le démarrage, l’arrêt, la configuration, les logs et les hosted services.
  • Partir par habitude de Host.CreateDefaultBuilder alors qu’il s’agit d’une nouvelle application
    • Sans nécessité de s’aligner sur du code existant, Host.CreateApplicationBuilder est le choix le plus naturel au départ.
  • Injecter directement un service scoped dans un BackgroundService
    • Les hosted services n’ont pas de scope par défaut. Créer un scope avec IServiceScopeFactory est plus sûr.
  • Un worker qui ne s’exécute qu’une fois, mais sans signaler son arrêt au host
    • Si vous faites un « run once » avec le modèle Worker, le host continue de tourner tant que vous n’appelez pas IHostApplicationLifetime.StopApplication() une fois le travail terminé.
  • Vouloir un arrêt propre mais couper avec Environment.Exit
    • Si vous utilisez un host, StopApplication() est la solution la plus cohérente quand vous voulez vous arrêter proprement.
  • Supposer le répertoire courant dans un Windows Service
    • La recherche de fichiers est plus stable si elle est ancrée sur IHostEnvironment.ContentRootPath.
  • Envelopper dès le départ un CLI de courte durée dans un BackgroundService
    • Pour un travail ponctuel, il suffit de résoudre et d’exécuter une classe de service ordinaire.
  • Insérer sans y réfléchir un timer à callback pour l’exécution périodique d’un BackgroundService
    • Si vous écrivez dans un flux async, PeriodicTimer est souvent plus lisible et moins sujet au désordre.

Avec le Generic Host, le simple fait de distinguer dès le départ s’il s’agit d’un job de courte durée ou d’un job résident réduit considérablement les hésitations.

10. Résumé

En une phrase, le Generic Host est le socle qui rassemble le point d’entrée et la gestion du cycle de vie d’une application .NET.

Reprenons les points à retenir.

  1. Le Generic Host inclut non seulement la DI, mais aussi la configuration, les logs, la gestion de l’arrêt et les hosted services
  2. Pour une nouvelle application non Web, Host.CreateApplicationBuilder(args) est le point de départ naturel
  3. Pour un job de courte durée, on peut se contenter de build et d’exécuter, sans utiliser BackgroundService
  4. Pour un traitement résident, BackgroundService combiné à la gestion du lifetime du host est très payant
  5. BackgroundService n’ayant pas de scope par défaut, il faut créer explicitement un scope pour les services scoped
  6. Le WebApplicationBuilder d’ASP.NET Core repose lui aussi, sur le plan conceptuel, sur le même flux

Le Generic Host n’est pas un outil pour un cérémonial pesant. Dès que la configuration, les logs, les dépendances, le démarrage et l’arrêt commencent, même légèrement, à se multiplier, c’est l’outil qui permet de les rassembler à l’entrée plutôt que de les laisser se disperser à l’intérieur des murs.

À l’inverse, pour un petit outil qui n’en a pas encore besoin, il n’est pas nécessaire de l’apporter. Une fois cette distinction acquise, le Generic Host cesse d’être « quelque chose qu’on ajoute par automatisme » pour devenir un socle pratique dont l’usage est clairement défini.

11. Références

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Qu'est-ce que le .NET Generic Host ?
Le Generic Host est le socle qui prend en charge, en un seul endroit, le démarrage et la durée de vie d'une application .NET. On y trouve l'injection de dépendances (DI), la configuration (appsettings.json, variables d'environnement, arguments de ligne de commande), la journalisation via ILogger<T>, les services hébergés comme IHostedService / BackgroundService, ainsi que la gestion de l'arrêt de l'application. Ce n'est pas un simple wrapper autour d'un conteneur DI : la façon de voir les choses la plus fiable consiste à le considérer comme le mécanisme qui rassemble le point d'assemblage de l'application et la gestion de son cycle de vie. Il montre toute son utilité dès que la configuration, les logs, les dépendances, le démarrage et l'arrêt commencent, même un peu, à s'accumuler.
Faut-il utiliser Host.CreateApplicationBuilder ou Host.CreateDefaultBuilder ?
Pour une nouvelle application non Web, il est naturel de partir de Host.CreateApplicationBuilder(args). Les deux offrent les mêmes fonctionnalités de base et le même comportement par défaut : il ne s'agit pas d'une nouvelle fonctionnalité d'un côté et d'une chose différente de l'autre. Ce qui diffère, c'est surtout le style d'écriture : CreateApplicationBuilder consiste à écrire directement dans builder.Services et consorts, tandis que CreateDefaultBuilder enchaîne des méthodes comme ConfigureServices. Si vous devez vous aligner sur du code existant ou une configuration reposant surtout sur les anciennes méthodes d'extension, choisissez CreateDefaultBuilder.
Le Generic Host vaut-il la peine d'être utilisé même dans une application console ?
Si vous voulez la DI, la configuration et les logs, le Generic Host est parfaitement utilisable même pour un outil console qui ne s'exécute qu'une seule fois. Il n'est pas obligatoire de créer un BackgroundService : appeler Build(), résoudre les services nécessaires, effectuer le travail puis quitter suffit déjà à profiter des avantages du Generic Host. À l'inverse, pour un petit outil qui se contente de lire des arguments une fois et d'afficher un résultat une fois, ou pour du code de vérification rapide, c'est excessif : ce n'est pas quelque chose à systématiquement embarquer.
Comment utiliser des services scoped dans un BackgroundService ?
Un BackgroundService n'a pas de scope par défaut, si bien qu'injecter directement un service scoped dans son constructeur n'est pas sûr. La méthode sûre consiste à injecter IServiceScopeFactory, à créer explicitement un scope dans ExecuteAsync, puis à résoudre le service du job à l'intérieur de ce scope. C'est un point à garder particulièrement à l'esprit si vous voulez utiliser un service scoped comme un DbContext.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog