Qu'est-ce que le .NET Generic Host ? - Le socle de la DI, de la configuration et des logs
· Mis à jour le: · Go Komura · 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+Cou à 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.CreateApplicationBuilderetHost.CreateDefaultBuilder? IHostest-il la même chose qu’un conteneur DI ?- Quel est son lien avec
BackgroundService? - Est-il différent du
WebApplicationBuilderd’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
- La conclusion d’abord (en une phrase)
- Les tableaux à consulter en premier
- 2.1. Ce que contient le Generic Host
- 2.2. Les différences entre les builders
- La vue d’ensemble du Generic Host (schéma)
- 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
- Configuration minimale
- 5.1. Exemple minimal dans une application console
- 5.2.
appsettings.json - 5.3. Ajouter un
BackgroundService
- 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
- Cas où il est adapté
- Cas où il ne convient pas / est excessif
- Pièges courants
- Résumé
- 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
WebApplicationBuilderd’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.
flowchart LR
Args["args / variables d'environnement / appsettings.json"] --> Builder["Host.CreateApplicationBuilder(args)"]
Builder --> Config["builder.Configuration"]
Builder --> Services["builder.Services"]
Builder --> Logging["builder.Logging"]
Services --> Hosted["IHostedService / BackgroundService"]
Builder --> Build["builder.Build()"]
Build --> Host["IHost"]
Host --> Run["Run / RunAsync"]
Run --> Lifetime["Démarrage / arrêt / Ctrl+C / SIGTERM"]
Lifetime --> Hosted
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>IConfigurationIHostEnvironmentIOptions<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.
- Le corps d’un
BackgroundServiceestExecuteAsync - 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
HttpClientou 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+Cou 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.Servicesbuilder.Configurationbuilder.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+Cou 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.CreateDefaultBuilderalors qu’il s’agit d’une nouvelle application- Sans nécessité de s’aligner sur du code existant,
Host.CreateApplicationBuilderest le choix le plus naturel au départ.
- Sans nécessité de s’aligner sur du code existant,
- Injecter directement un service scoped dans un
BackgroundService- Les hosted services n’ont pas de scope par défaut. Créer un scope avec
IServiceScopeFactoryest plus sûr.
- Les hosted services n’ont pas de scope par défaut. Créer un scope avec
- 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é.
- Si vous faites un « run once » avec le modèle Worker, le host continue de tourner tant que vous n’appelez pas
- 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.
- Si vous utilisez un host,
- Supposer le répertoire courant dans un Windows Service
- La recherche de fichiers est plus stable si elle est ancrée sur
IHostEnvironment.ContentRootPath.
- La recherche de fichiers est plus stable si elle est ancrée sur
- 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,PeriodicTimerest souvent plus lisible et moins sujet au désordre.
- Si vous écrivez dans un flux
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.
- Le Generic Host inclut non seulement la DI, mais aussi la configuration, les logs, la gestion de l’arrêt et les hosted services
- Pour une nouvelle application non Web,
Host.CreateApplicationBuilder(args)est le point de départ naturel - Pour un job de courte durée, on peut se contenter de build et d’exécuter, sans utiliser
BackgroundService - Pour un traitement résident,
BackgroundServicecombiné à la gestion du lifetime du host est très payant BackgroundServicen’ayant pas de scope par défaut, il faut créer explicitement un scope pour les services scoped- Le
WebApplicationBuilderd’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
- .NET Generic Host - .NET
- Worker Services in .NET
- Use scoped services within a BackgroundService - .NET
- Configuration in .NET
- Options pattern in .NET
- .NET Generic Host in ASP.NET Core
- Create Windows Service using BackgroundService - .NET
- Article associé : Comment choisir entre PeriodicTimer, System.Threading.Timer et DispatcherTimer - remettre de l’ordre dans l’exécution périodique en .NET
- Article associé : Les bonnes pratiques async/await en C# - tableau de décision pour Task.Run et ConfigureAwait
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Pourquoi utiliser le Generic Host .NET et BackgroundService dans une application de bureau
Comment utiliser le Generic Host et BackgroundService pour organiser le démarrage, le traitement périodique, l'arrêt, la journalisation, ...
Au-delà de appsettings.json — guide pratique de la gestion de configuration dans les applications métier Windows (environnements, secrets et emplacements d'écriture)
Guide pratique de la configuration des applications métier Windows : superposition de appsettings.json, choix entre IConfiguration et IOp...
Comment choisir la communication inter-processus sous Windows ── Tableau de décision : tubes nommés / TCP / gRPC / mémoire partagée / COM
Comment choisir le moyen de faire communiquer des applications Windows entre elles ? Cet article organise les tubes nommés, le TCP local,...
Comment créer et exploiter un service Windows ── du choix entre Planificateur de tâches et service à la transformation d'un BackgroundService en service Windows
Faut-il transformer un traitement résident en service Windows, ou le Planificateur de tâches suffit-il ? Ce guide organise, du point de v...
Qu'est-ce que .NET Native AOT ? - Les différences avec JIT et le trimming
Ce qu'est Native AOT en .NET, expliqué à travers ses différences avec JIT, ReadyToRun, self-contained, single-file, trimming et les sourc...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Generic Host et architecture d'application
Generic Host, BackgroundService, injection de dépendances, configuration, journalisation et cycle de vie.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Construire des applications Windows avec traitement résident, gestion de l'arrêt, journalisation et configuration est exactement le type de travail d'implémentation qui correspond à notre service de développement d'applications Windows.
Conseil technique et revue de conception
Si vous souhaitez clarifier la DI, les durées de vie et la séparation des responsabilités avant l'implémentation, nous pouvons commencer par définir les orientations dans le cadre d'un conseil technique et d'une revue de conception.
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.
Liens publics