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.
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 :
Maintenant, essayons de reproduire le même résultat dans une application .NET CORE utilisant le pattern IOptions
.
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 :
appSettings.json
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.appSettings.json
startup.cs
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.
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
.
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; }
}
}
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>();
}
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 :
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>()
.