Dans les applications .NET, la configuraton est généralement gérée via le fichier appsettings.json. Cependant, il est souvent nécessaire d'intégrer des sources de configuration additionnelles ou de définir des paramètres de manière dynamique. Cet article explore diverses méthodes pour étendre et personnaliser la gession de la configuration, en offrant plus de flexibilité que le simple fichier par défaut.
- Intégration de Fichiers JSON Personnalisés
Le système de configuration de .NET permet d'ajouter facilement des fichiers JSON supplémentaires pour organiser vos paramètres. Ces fichiers peuvent inclure des configurations spécifiques à un environnement, des modules ou des paramètres génériques.
1.1 Configuration Globale via IHostBuilder
La méthode la plus courante pour étendre la configuration d'une application entière est d'utiliser ConfigureAppConfiguration lors de la construction de l'hôte principal. Cela permet d'ajouter des fournisseurs de configuraton avant que l'application ne démarre, garantissant ainsi que ces paramètres sont disponibles partout.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostContext, config) =>
{
var environment = hostContext.HostingEnvironment;
// Ajout d'un fichier de configuration spécifique au développement, obligatoire.
// Il sera chargé si présent, sinon le démarrage échouera.
config.AddJsonFile("settings.dev.json", optional: false, reloadOnChange: true);
// Ajout d'un fichier de configuration commun à plusieurs modules, non obligatoire.
// Utile pour des paramètres partagés qui ne sont pas critiques.
config.AddJsonFile("global.components.json", optional: true, reloadOnChange: true);
// Ajout d'un fichier spécifique à l'environnement, avec un nom personnalisé.
// Les fichiers appsettings.json et appsettings.{env}.json sont déjà inclus par CreateDefaultBuilder.
config.AddJsonFile($"application.{environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Il est crucial de s'assurer que les fichiers de configuration personnalisés sont copiés dans le répertoire de sortie de l'application (par exemple, en configurant "Copy to Output Directory: Copy always" ou "Copy if newer" dans les propriétés du fichier dans Visual Studio).
1.2 Construction d'une Configuration Indépendante
Dans certains cas, vous pourriez avoir besoin d'une instance IConfiguration isolée pour une bibliothèque ou un service spécifique, distincte de la configuration globale de l'application. Cela peut être réalisé en construisant un ConfigurationBuilder indépendant.
using Microsoft.Extensions.Configuration;
using System;
public class CustomConfigLoader
{
public static IConfiguration BuildModuleSpecificConfiguration(string baseDirectory, string moduleName)
{
return new ConfigurationBuilder()
.SetBasePath(baseDirectory)
.AddJsonFile($"{moduleName.ToLowerInvariant()}.config.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables($"{moduleName.ToUpperInvariant()}_") // Variables d'environnement préfixées
.Build();
}
}
// Exemple d'utilisation dans une classe nécessitant sa propre configuration
public class DataProcessingService
{
private readonly IConfiguration _moduleConfiguration;
public DataProcessingService()
{
_moduleConfiguration = CustomConfigLoader.BuildModuleSpecificConfiguration(
AppDomain.CurrentDomain.BaseDirectory,
"DataProcessor"
);
string dataApiKey = _moduleConfiguration["ApiKey"];
Console.WriteLine($"Clé API du processeur de données : {dataApiKey}");
// ... utilisez la configuration spécifique au service
}
}
- Utilisation du Fournisseur de Configuration en Mémoire (Memory Provider)
Le fournisseur de configuration en mémoire permet d'injecter des paires clé-valeur directement dans le système de configuration sans utiliser de fichiers physiques. C'est utile pour des paramètres dynamiques, des tests unitaires, ou des valeurs passées au démarrage de l'application. Les valeurs en mémoire ont généralement une priorité élevée et peuvent surcharger les valeurs provenant d'autres sources.
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostContext, config) =>
{
var applicationOverrides = new Dictionary<string, string>
{
{"ServiceSettings:RetryCount", "3"},
{"ServiceSettings:TimeoutMs", "5000"},
{"FeatureManagement:AdvancedReporting", "True"},
{"SystemEnvironment", hostContext.HostingEnvironment.EnvironmentName}
};
config.AddInMemoryCollection(applicationOverrides);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
- Accès Structuré à la Configuration via des POCOs et
IOptions
Plutôt que d'accéder directement aux valeurs de configuration par des chaînes de caractères (par exemple, Configuration["MySection:MyKey"]), il est recommandé d'utiliser des classes POCO (Plain Old C# Objects) et l'interface IOptions<T>. Cette approche offre une meilleure typisation, facilite la validation et rend le code plus robuste et maintenable.
3.1 Définition des Classes POCO
Créez des classes qui représentent les sections de votre configuration. Par exemple, pour une section "ApiGateways" :
public class ApiGatewaySettings
{
public string ProductServiceUrl { get; set; }
public string CustomerServiceUrl { get; set; }
public int DefaultRequestTimeoutSeconds { get; set; } = 60; // Valeur par défaut
}
public class LogSettings
{
public string LogFilePath { get; set; } = "logs/application.log";
public string LogLevel { get; set; } = "Information";
}
3.2 Enregistrement de la Configuration dans ConfigureServices
Dans la méthode ConfigureServices de votre classe Startup (ou directement dans Program.cs avec .NET 6+), liez les sections de configuration à vos classes POCO :
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Lier la section "ApiGateways" à la classe ApiGatewaySettings
services.Configure<ApiGatewaySettings>(Configuration.GetSection("ApiGateways"));
// Lier la section "Logging" à la classe LogSettings
services.Configure<LogSettings>(Configuration.GetSection("Logging"));
// ... autres services
}
// ... Configure method
}
Dans appsettings.json, ces sections pourraient ressembler à ceci :
{
"ApiGateways": {
"ProductServiceUrl": "https://api.example.com/products",
"CustomerServiceUrl": "https://api.example.com/customers",
"DefaultRequestTimeoutSeconds": 90
},
"Logging": {
"LogFilePath": "C:/logs/myapp.log",
"LogLevel": "Debug"
},
"NotificationChannels": [
{
"Name": "Email",
"IsEnabled": true,
"Address": "admin@example.com"
},
{
"Name": "SMS",
"IsEnabled": false
}
]
}
3.3 Utilisation dans les Services et Contrôleurs
Injectez IOptions<T> dans le constructeur de vos services ou contrôleurs pour accéder aux valeurs de configuration. Le framework gérera l'instanciation de T et la liaison des valeurs.
using Microsoft.Extensions.Options;
using System;
public class ProductManagementService
{
private readonly ApiGatewaySettings _apiSettings;
private readonly LogSettings _logSettings;
public ProductManagementService(IOptions<ApiGatewaySettings> apiOptions, IOptions<LogSettings> logOptions)
{
_apiSettings = apiOptions.Value;
_logSettings = logOptions.Value;
}
public void FetchProducts()
{
Console.WriteLine($"Utilisation de l'URL du service produit : {_apiSettings.ProductServiceUrl}");
Console.WriteLine($"Timeout par défaut : {_apiSettings.DefaultRequestTimeoutSeconds}s");
Console.WriteLine($"Niveau de journalisation configuré : {_logSettings.LogLevel}");
// ... Logique pour appeler le service produit
}
}
3.4 Récupération de Listes ou Collections Dynamiques
Pour récupérer une liste d'objets, vous pouvez utiliser la méthode générique Get<T>() directement sur une section de IConfiguration. Cela est particulièrement utile pour des collections dynamiques qui ne nécessitent pas une surveillance des changements via IOptionsSnapshot ou IOptionsMonitor.
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
// POCO pour les éléments de la liste
public class NotificationChannel
{
public string Name { get; set; }
public bool IsEnabled { get; set; }
public string Address { get; set; } // Optionnel
}
// Dans un service ou contrôleur, avec IConfiguration injecté
public class NotificationService
{
private readonly IConfiguration _configuration;
public NotificationService(IConfiguration configuration)
{
_configuration = configuration;
}
public void SendNotifications()
{
// Nécessite le package 'Microsoft.Extensions.Configuration.Binder'
var channels = _configuration.GetSection("NotificationChannels").Get<List<NotificationChannel>>();
if (channels != null)
{
foreach (var channel in channels)
{
if (channel.IsEnabled)
{
Console.WriteLine($"Envoi de notification via le canal : {channel.Name} à {channel.Address}");
}
}
}
}
}