Utiliser (get/set) appSettings avec .Net Core

Coding

Le framework .Net fournit, dans le fichier de configuration, la section appSettings sous forme d’une paire clé/valeur pour gérer certaines propriétés de configurations d’une application. Le framework .Net Core a étendu cette possibilité en utilisant le pattern IOptions<out TOptions>.

Nous allons voir dans ce billet un exemple de configuration utilisation de ce pattern et comment les utiliser par injection dans vos services et/ou dans vos controllers.

Section appSettings et ConfigurationManager

Ci-après un exemple d’utilisation de la section appSettings dans le fichier de configuration d’une application.

<configuration>
    .
    .
    .
    <appSettings>
        <add key="Environnement" value="DEV"/>
        <add key="Nom" value="AppSettingsApp"/>
        <add key="Description" value="Test d'utilisation des AppSettings .NET"/>
        <add key="Major" value="1"/>
        <add key="Minor" value="0"/>
        <add key="patch" value="0"/>
  </appSettings>
    .
    .
    .
</configuration>

L’accès aux propriétés de la section se fait par la classe statique ConfigurationManager. Son utilisation dans le code :

using System;
using System.Configuration;

namespace TestAppSettings {
    class Program  {
        static void Main(string[] args) {

            var environnement = ConfigurationManager.AppSettings["Environnement"];
            var nom = ConfigurationManager.AppSettings["Nom"];
            var description = ConfigurationManager.AppSettings["Description"];

            var major = ConfigurationManager.AppSettings["Major"];
            var minor = ConfigurationManager.AppSettings["Minor"];
            var patch = ConfigurationManager.AppSettings["Patch"];

            Console.WriteLine($"Hello {nom} - {environnement}");
            Console.WriteLine(description);
            Console.WriteLine($"version :  {major}.{minor}.{patch}");

            Console.ReadLine();
        }
    }
}

Le résultat : app.config

Maintenant, essayons de reproduire le même résultat dans une application .NET CORE utilisant le pattern IOptions.

Déclaration des sections de configuration via IOptions

Le pattern options permet de gérer plusieurs types de configurations d’applications mais on se focalisera uniquement sur la section appSettings dans ce billet.

L’intérface IOptions<out TOptions> fournit un moyen plus élégant de retrouver les valeurs de la section appSettings d’un fichier de configuration. Ci-après les étapes à suivre pour générer et utiliser votre section de configuration :

  1. Créer un fichier appSettings.json
  2. Créer un fichier appSettings.{env}.json ou env correspond au nom de votre environnement (ex: production, integration, etc.) si vous gérez des valeurs différentes par environnement.
  3. Créer les classes corréspondantes aux sections que vous avez déclarées dans le fichier appSettings.json
  4. Configurer les sections dans le fichier startup.cs
  5. Injecter et utiliser les sections.

Créer un fichier appSettings.json

Définissez chacune de vos sections dans le fichier comme suit :

{
    "MyApp": {
        "Nom": "AppSettingsApp",
        "Description": "Test d'utilisation des AppSettings .NET",
        "Environnement": "DEV",
        "Version": {
            "Major": "1",
            "Minor": "0",
            "Patch": "0"
        }
  }
}

Comme vous pouvez le constater sur la définition ci-dessous, on a déclaré 3 propriétés dont une qui est de type complexe.

Créer un fichier appSettings.{env}.json

Créer l’équivalent du fichier appSettings.json pour chaque environnement que vous voulez gérer et settez les valeurs des propriétés pour l’environnement en question. Dans l’exemple ci-après, j’ai un environnement de production.

appSettings.production.json

Créer les classes corréspondantes

namespace TestAppSettings {
    public class MyApp {
        public string Nom { get; set; }
        public string Description { get; set; }
        public string Environnement { get; set; }
        public Version Version { get; set; }
    }
}
namespace TestAppSettings {
    public class Version {
        public string Major { get; set; }
        public int Minor { get; set; }
        public string Patch { get; set; }
    }
}

Configurer les sections dans la classe startup.cs

Ci-après le contenu minimal du fichier :

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace TestAppSettings
{
    public class Startup {
        public Startup(IHostingEnvironment env) {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("AppSettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"AppSettings.{env.EnvironmentName}.json", optional: true);

            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services) {
            services.AddOptions();
            services.Configure<MyApp>(Configuration.GetSection("MyApp"));
            services.AddSingleton<ITestSettings, TestSettings>();
        }

        // Use this method to configure the HTTP request pipeline
        public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
            app.UseMvc();
        }
    }
}

Pour commencer, il faut inclure les fichiers AppSettings.json lors de la construction de la configuration de l’application :

.AddJsonFile("AppSettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"AppSettings.{env.EnvironmentName}.json", optional: true);

Le champ optional: true indique que l’existance du fichier est optionel et ne bloque pas le chargement de l’application et le reloadOnChange parle de lui-même non ?

Ensuite, il faut activer le service de gestion des options et configurer chaque section que vous utilisez. Dans notre cas, on a uniquement la section MyApp.

public void ConfigureServices(IServiceCollection services) {
    services.AddOptions();
    services.Configure<MyApp>(Configuration.GetSection("MyApp"));
}

La dernière ligne de configuration nous dit que nous voulons une instance unique du service ITestSettings qui utilise le pattern IOptions par injection de dépendances. On verra dans la partie suivante comment notre section est injectée et utilisée.

public void ConfigureServices(IServiceCollection services) {
    ...
    services.AddSingleton<ITestSettings, TestSettings>();
}

Injecter et utiliser

Pour la simplification de l’article, on est partie sur une application console avec le service ITestSettings qui contient notre logique applicatif et qui utilise l’injection de dépendance pour injecter notre section de configuration. Ci-après l’implémentation de ce service :

public interface ITestSettings {
    void DisplaySettings();
}
public class TestSettings : ITestSettings {
    readonly IOptions<MyApp> _app;

    public TestSettings(IOptions<MyApp> app) {
        _app = app;
    }

    public void DisplaySettings() {
        Console.WriteLine($"Hello {_app.Value.Nom} - {_app.Value.Environnement}");
        Console.WriteLine(_app.Value.Description);
        Console.WriteLine($"Version : {_app.Value.Version.Major}.{_app.Value.Version.Minor}.{_app.Value.Version.Patch}");
    }
}

Comme vous pouvez le constater, lors de la création de l’instance du service ITestSettings, on voudrait utiliser les valeurs de la section MyApp et celà par injection de dépendance.

Et dans le fichier Program.cs :

 public class Program {
    public static void Main(string[] args) {
        HostingEnvironment env = new HostingEnvironment();
        env.ContentRootPath = Directory.GetCurrentDirectory();
        env.EnvironmentName = "Development";

        Startup startup = new Startup(env);
        ServiceCollection sc = new ServiceCollection();
        startup.ConfigureServices(sc);
        var ServiceProvider = sc.BuildServiceProvider();

        var settings = ServiceProvider.GetService<ITestSettings>();
        settings.DisplaySettings();

        Console.ReadLine();
    }
}

Toute la première partie de la méthode Main concerne la configuration du host pour démarrer correctement l’application. On configure le service provider pour la résolution des services et la hiérarchie des injections.

HostingEnvironment env = new HostingEnvironment();
env.ContentRootPath = Directory.GetCurrentDirectory();
env.EnvironmentName = "Development";

Startup startup = new Startup(env);
ServiceCollection sc = new ServiceCollection();
startup.ConfigureServices(sc);
var ServiceProvider = sc.BuildServiceProvider();

Ensuite, on invoque le service et la magie s’opère …

var settings = ServiceProvider.GetService<ITestSettings>();
settings.DisplaySettings();

Le résultat : IOptions

Remarque

Dans le cas d’une application ASP.NET CORE (Web API ou MVC), on choisirait plutôt un WebHost et l’inclusion du fichier startup.cs se fera de la façon suivante :

var host = new WebHostBuilder()
    .UseKestrel()
    .UseUrls("http://localhost:5000")
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

host.Run();

Le point d’inclusion du builder étant le .UseStartup<Startup>().