Passer au contenu du pied de page
Iron Academy Logo
Apprendre le C#
Apprendre le C#

Autres catégories

Le modèle Options en C#

[[academy-video-youtube({"vid": "ko1Ie9gDydY", "start_time": "0", "title": "The Options Pattern in C#", "creator": "Tim Corey", "length": "10m 15s"})]]

La configuration dans les applications .NET se trouve généralement dans appsettings.json, les variables d'environnement, ou les secrets utilisateur. Obtenir ces données de configuration d'un fichier et les intégrer dans vos classes de manière propre et testable est ce que le modèle d'options résout. Au lieu de lire manuellement des clés JSON ou de transmettre des chaînes brutes, vous liez une section de configuration à une classe C# fortement typée et laissez l'injection de dépendances la livrer là où elle est nécessaire.

Dans sa vidéo "Le modèle Options dans C#", Tim Corey présente les trois variations du modèle d'options (IOptions, IOptionsSnapshot, et IOptionsMonitor), démontre chacune dans une application serveur Blazor, et montre comment ajouter une validation pour que votre application échoue rapidement lorsque la configuration est manquante ou mal formée. Si vous construisez tout projet .NET qui lit à partir de fichiers de configuration et utilise l'injection de dépendances, ce modèle est fondamental.

La configuration : Un modèle POCO et appsettings.json

[0:34 - 1:46] Tim démarre avec une application web Blazor (rendu côté serveur, pas d'interactivité côté client) qui a déjà deux éléments en place. La première est une section dans appsettings.json avec trois paires clé-valeur sous une section CloudInfo :

// appsettings.json
{
  "CloudInfo": {
    "Storage": "https://storage.example.com",
    "Website": "https://www.example.com",
    "API": "https://api.example.com"
  }
}
// appsettings.json
{
  "CloudInfo": {
    "Storage": "https://storage.example.com",
    "Website": "https://www.example.com",
    "API": "https://api.example.com"
  }
}

La deuxième pièce est une classe C# simple (un POCO) dont les propriétés reflètent les clés JSON :

public class CloudInfoOptions
{
    public string Storage { get; set; }
    public string Website { get; set; }
    public string API { get; set; }
}
public class CloudInfoOptions
{
    public string Storage { get; set; }
    public string Website { get; set; }
    public string API { get; set; }
}

Tim note que la source de configuration n'a pas besoin d'être appsettings.json spécifiquement. Elle pourrait être appsettings.Development.json, secrets.json, ou toute combinaison. Le modèle d'options lit à partir des fournisseurs de configuration enregistrés dans l'application, et les noms de propriétés sur le POCO correspondent aux clés JSON par convention.

Enregistrement des options dans Program.cs

[2:25 - 3:15] Brancher la configuration à l'injection de dépendances nécessite deux appels de méthode dans Program.cs :

// Register CloudInfoOptions bound to the "CloudInfo" section
builder.Services.AddOptions<CloudInfoOptions>()
    .BindConfiguration("CloudInfo");
// Register CloudInfoOptions bound to the "CloudInfo" section
builder.Services.AddOptions<CloudInfoOptions>()
    .BindConfiguration("CloudInfo");

AddOptions<t>() enregistre le type avec DI. BindConfiguration("CloudInfo") indique au framework quelle section de la configuration mapper. La chaîne "CloudInfo" correspond à la clé JSON dans appsettings.json. Si le nom de la section et le nom de la classe ne correspondent pas, l'argument chaîne est ce que le framework utilise pour trouver les bonnes données.

IOptions : L'approche singleton

[3:15 - 5:18] With the registration in place, Tim injects the configuration into a Blazor page using IOptions<CloudInfoOptions>:

@inject IOptions<CloudInfoOptions> CloudConfig

@code {
    protected override void OnInitialized()
    {
        var options = CloudConfig.Value;
        // options.Storage, options.Website, options.API are available
    }
}
@inject IOptions<CloudInfoOptions> CloudConfig

@code {
    protected override void OnInitialized()
    {
        var options = CloudConfig.Value;
        // options.Storage, options.Website, options.API are available
    }
}

Le détail crucial est que IOptions<t> est enregistré comme un singleton. Les valeurs de configuration sont lues une fois lorsque l'application démarre et mises en cache pour la durée de vie du processus. Si vous changez appsettings.json pendant que l'application est en cours d'exécution, IOptions ne reflétera pas ces changements.

Pour la plupart des applications, c'est le bon choix. La configuration change rarement en cours d'exécution, et la durée de vie singleton signifie zéro surcharge par requête.

IOptionsSnapshot : Rechargement en portée

[5:18 - 6:55] Tim échange IOptions pour IOptionsSnapshot pour démontrer la variante à portée :

@inject IOptionsSnapshot<CloudInfoOptions> CloudConfig
@inject IOptionsSnapshot<CloudInfoOptions> CloudConfig

IOptionsSnapshot<t> lit la configuration fraîchement pour chaque requête (durée de vie à portée). Si vous modifiez appsettings.json pendant que l'application est en cours d'exécution, la prochaine requête web adoptera les nouvelles valeurs. La requête précédente conserve son propre instantané, donc il n'y a pas d'incohérence en cours de requête.

Cela est utile pour les applications où les modifications de configuration doivent prendre effet sans redémarrage, telles que l'activation des drapeaux de fonctionnalité, la mise à jour des points de terminaison d'API, ou la rotation des chaînes de connexion de stockage. Le compromis est un petit coût par requête pour relire la configuration, ce qui est négligeable pour la plupart des charges de travail.

IOptionsMonitor: Notifications de changement en direct

[6:55 - 8:34] La troisième variante, IOptionsMonitor<t>, va plus loin que le rechargement de l'instantané. Elle surveille activement les changements de configuration et peut déclencher des rappels lorsque les valeurs sont mises à jour :

@inject IOptionsMonitor<CloudInfoOptions> CloudConfig

@code {
    protected override void OnInitialized()
    {
        // Current values
        var current = CloudConfig.CurrentValue;

        // Register a callback for live changes
        CloudConfig.OnChange(updatedOptions =>
        {
            // React to configuration changes in real time
        });
    }
}
@inject IOptionsMonitor<CloudInfoOptions> CloudConfig

@code {
    protected override void OnInitialized()
    {
        // Current values
        var current = CloudConfig.CurrentValue;

        // Register a callback for live changes
        CloudConfig.OnChange(updatedOptions =>
        {
            // React to configuration changes in real time
        });
    }
}

Contrairement à IOptionsSnapshot, qui ne se met à jour qu'entre les requêtes, IOptionsMonitor peut détecter les changements pendant une opération de longue durée. Tim souligne que cela est le plus pertinent pour les services en arrière-plan, les hubs SignalR, ou tout composant avec une durée de vie plus longue qu'une seule requête HTTP.

Ajouter une validation

[8:34 - 10:05] La dernière partie que Tim aborde est la validation. Le modèle d'options prend en charge les règles de validation inline qui fonctionnent lorsqu'on accède pour la première fois à la configuration :

builder.Services.AddOptions<CloudInfoOptions>()
    .BindConfiguration("CloudInfo")
    .Validate(opts =>
        !string.IsNullOrEmpty(opts.Storage) &&
        !string.IsNullOrEmpty(opts.Website) &&
        !string.IsNullOrEmpty(opts.API));
builder.Services.AddOptions<CloudInfoOptions>()
    .BindConfiguration("CloudInfo")
    .Validate(opts =>
        !string.IsNullOrEmpty(opts.Storage) &&
        !string.IsNullOrEmpty(opts.Website) &&
        !string.IsNullOrEmpty(opts.API));

Tim démontre le cas d'échec en retirant la valeur Storage de appsettings.json et en lançant l'application. Le résultat est une exception non gérée immédiate : "CloudInfoOptions a échoué à la validation." L'application refuse de démarrer avec une configuration incomplète, ce qui est exactement le comportement souhaité. Découvrir une valeur manquante au démarrage est bien mieux que de rencontrer une référence nulle en production à 2 h du matin.

Pour des scénarios de validation plus complexes (vérifications inter-propriétés, règles conditionnelles), vous pouvez chaîner plusieurs appels .Validate() ou implémenter IValidateOptions<t> comme une classe de validation dédiée.

Conclusion : Trois interfaces, un modèle

[10:05 - 10:10] Le modèle d'options offre une séparation nette entre le stockage de la configuration et sa consommation. IOptions couvre la majorité des cas d'utilisation où la configuration est statique. IOptionsSnapshot gère les applications qui doivent récupérer les changements entre les requêtes. IOptionsMonitor sert aux composants de longue durée qui nécessitent une prise de conscience en temps réel des mises à jour de configuration. Et la validation garantit que votre application échoue de manière bruyante lorsque des valeurs requises sont manquantes.

Conclusion

[10:10 - 10:15] Pour résumer : enregistrez votre configuration avec AddOptions<t>().BindConfiguration("SectionName") dans Program.cs, injectez une des trois interfaces (IOptions, IOptionsSnapshot, ou IOptionsMonitor) selon vos besoins de rechargement, et ajoutez .Validate() pour capturer les valeurs manquantes au démarrage plutôt qu'à l'exécution.

Le modèle fonctionne dans n'importe quel type de projet .NET : Blazor, ASP.NET Core, applications console, services de travail. L'enregistrement est le même partout.

Conseil Exemple : Si vous n'êtes pas sûr de quelle interface utiliser, commencez avec IOptions<t>. C'est la plus simple, sans surcharge par requête et elle couvre la grande majorité des cas où la configuration est définie au démarrage et ne change pas. Ne passez à IOptionsSnapshot ou IOptionsMonitor que lorsque vous avez un besoin concret de rechargement à l'exécution.

Regardez la vidéo complète sur sa chaîne YouTube Channel pour obtenir plus d'informations sur les modèles de configuration C#.

Hero Worlddot related to Le modèle Options en C#
Hero Affiliate related to Le modèle Options en C#

Gagnez plus en partageant ce que vous aimez

Vous créez du contenu pour les développeurs travaillant avec .NET, C#, Java, Python ou Node.js ? Transformez votre expertise en revenu supplémentaire !

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi