Modifier app.config par défaut à l’exécution

J’ai le problème suivant:
Nous avons une application qui charge des modules (ajouts). Ces modules peuvent nécessiter des entrées dans app.config (par exemple, configuration WCF). Les modules étant chargés dynamicment, je ne souhaite pas avoir ces entrées dans le fichier app.config de mon application.
Ce que je voudrais faire est le suivant:

  • Créer un nouvel app.config en mémoire qui intègre les sections de configuration des modules
  • Dites à mon application d’utiliser cette nouvelle application.config

Note: Je ne veux pas écraser app.config par défaut!

Il devrait fonctionner de manière transparente, par exemple si ConfigurationManager.AppSettings utilise ce nouveau fichier.

Au cours de mon évaluation de ce problème, j’ai proposé la même solution que celle fournie ici: Rechargez app.config avec nunit .
Malheureusement, cela ne semble rien faire, car je reçois toujours les données du fichier app.config normal.

J’ai utilisé ce code pour le tester:

 Console.WriteLine(ConfigurationManager.AppSettings["SettingA"]); Console.WriteLine(Settings.Default.Setting); var combinedConfig = ssortingng.Format(CONFIG2, CONFIG); var tempFileName = Path.GetTempFileName(); using (var writer = new StreamWriter(tempFileName)) { writer.Write(combinedConfig); } using(AppConfig.Change(tempFileName)) { Console.WriteLine(ConfigurationManager.AppSettings["SettingA"]); Console.WriteLine(Settings.Default.Setting); } 

Il imprime les mêmes valeurs twice, bien que combinationConfig contienne d’autres valeurs que le app.config normal.

Le hack dans la question liée fonctionne si elle est utilisée avant que le système de configuration ne soit utilisé pour la première fois. Après cela, ça ne marche plus.
La raison:
Il existe une classe ClientConfigPaths qui met en cache les chemins. Ainsi, même après avoir modifié le chemin avec SetData , il n’est pas relu car il existe déjà des valeurs mises en cache. La solution consiste à les supprimer aussi:

 using System; using System.Configuration; using System.Linq; using System.Reflection; public abstract class AppConfig : IDisposable { public static AppConfig Change(ssortingng path) { return new ChangeAppConfig(path); } public abstract void Dispose(); private class ChangeAppConfig : AppConfig { private readonly ssortingng oldConfig = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToSsortingng(); private bool disposedValue; public ChangeAppConfig(ssortingng path) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path); ResetConfigMechanism(); } public override void Dispose() { if (!disposedValue) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", oldConfig); ResetConfigMechanism(); disposedValue = true; } GC.SuppressFinalize(this); } private static void ResetConfigMechanism() { typeof(ConfigurationManager) .GetField("s_initState", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, 0); typeof(ConfigurationManager) .GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, null); typeof(ConfigurationManager) .Assembly.GetTypes() .Where(x => x.FullName == "System.Configuration.ClientConfigPaths") .First() .GetField("s_current", BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, null); } } } 

L’utilisation est comme ceci:

 // the default app.config is used. using(AppConfig.Change(tempFileName)) { // the app.config in tempFileName is used } // the default app.config is used. 

Si vous voulez changer le app.config utilisé pour toute l’exécution de votre application, placez simplement AppConfig.Change(tempFileName) sans utiliser quelque part au début de votre application.

Vous pouvez essayer d’utiliser Configuration et Ajouter ConfigurationSection à l’exécution

 Configuration applicationConfiguration = ConfigurationManager.OpenMappedExeConfiguration( new ExeConfigurationFileMap(){ExeConfigFilename = path_to_your_config, ConfigurationUserLevel.None ); applicationConfiguration.Sections.Add("section",new YourSection()) applicationConfiguration.Save(ConfigurationSaveMode.Full,true); 

EDIT: Voici une solution basée sur la reflection (pas très bien cependant)

Créer une classe dérivée de IInternalConfigSystem

 public class ConfigeSystem: IInternalConfigSystem { public NameValueCollection Settings = new NameValueCollection(); #region Implementation of IInternalConfigSystem public object GetSection(ssortingng configKey) { return Settings; } public void RefreshConfig(ssortingng sectionName) { //throw new NotImplementedException(); } public bool SupportsUserConfig { get; private set; } #endregion } 

puis par reflection définissez-le sur le champ privé dans ConfigurationManager

  ConfigeSystem configSystem = new ConfigeSystem(); configSystem.Settings.Add("s1","S"); Type type = typeof(ConfigurationManager); FieldInfo info = type.GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static); info.SetValue(null, configSystem); bool res = ConfigurationManager.AppSettings["s1"] == "S"; // return true 

La solution @Daniel fonctionne correctement. Une solution similaire avec plus d’explications est en coin c-sharp. Pour être complet, j’aimerais partager ma version: avec l’ using , et les indicateurs de bits abrégés.

 using System;//AppDomain using System.Linq;//Where using System.Configuration;//app.config using System.Reflection;//BindingFlags ///  /// Use your own App.Config file instead of the default. ///  ///  public static void ChangeAppConfig(ssortingng NewAppConfigFullPathName) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", NewAppConfigFullPathName); ResetConfigMechanism(); return; } ///  /// Remove cached values from ClientConfigPaths. /// Call this after changing path to App.Config. ///  private static void ResetConfigMechanism() { BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Static; typeof(ConfigurationManager) .GetField("s_initState", Flags) .SetValue(null, 0); typeof(ConfigurationManager) .GetField("s_configSystem", Flags) .SetValue(null, null); typeof(ConfigurationManager) .Assembly.GetTypes() .Where(x => x.FullName == "System.Configuration.ClientConfigPaths") .First() .GetField("s_current", Flags) .SetValue(null, null); return; } 

La solution de Daniel semble fonctionner même pour les assemblages en aval. J’avais déjà utilisé AppDomain.SetData auparavant, mais je ne savais pas comment réinitialiser les indicateurs de configuration internes.

Converti en C ++ / CLI pour les intéressés

 ///  /// Remove cached values from ClientConfigPaths. /// Call this after changing path to App.Config. ///  void ResetConfigMechanism() { BindingFlags Flags = BindingFlags::NonPublic | BindingFlags::Static; Type ^cfgType = ConfigurationManager::typeid; Int32 ^zero = gcnew Int32(0); cfgType->GetField("s_initState", Flags) ->SetValue(nullptr, zero); cfgType->GetField("s_configSystem", Flags) ->SetValue(nullptr, nullptr); for each(System::Type ^t in cfgType->Assembly->GetTypes()) { if (t->FullName == "System.Configuration.ClientConfigPaths") { t->GetField("s_current", Flags)->SetValue(nullptr, nullptr); } } return; } ///  /// Use your own App.Config file instead of the default. ///  ///  void ChangeAppConfig(Ssortingng ^NewAppConfigFullPathName) { AppDomain::CurrentDomain->SetData(L"APP_CONFIG_FILE", NewAppConfigFullPathName); ResetConfigMechanism(); return; } 

Si quelqu’un est intéressé, voici une méthode qui fonctionne sur Mono.

 ssortingng configFilePath = ".../App"; System.Configuration.Configuration newConfiguration = ConfigurationManager.OpenExeConfiguration(configFilePath); FieldInfo configSystemField = typeof(ConfigurationManager).GetField("configSystem", BindingFlags.NonPublic | BindingFlags.Static); object configSystem = configSystemField.GetValue(null); FieldInfo cfgField = configSystem.GetType().GetField("cfg", BindingFlags.Instance | BindingFlags.NonPublic); cfgField.SetValue(configSystem, newConfiguration); 

Si votre fichier de configuration est simplement écrit avec la clé / les valeurs dans “appSettings”, vous pouvez alors lire un autre fichier avec un tel code:

 System.Configuration.ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap(); configFileMap.ExeConfigFilename = configFilePath; System.Configuration.Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None); AppSettingsSection section = (AppSettingsSection)configuration.GetSection("appSettings"); 

Vous pouvez ensuite lire section.Settings en tant que collection de KeyValueConfigurationElement.

Super discussion, j’ai ajouté plus de commentaires à la méthode ResetConfigMechanism pour comprendre la magie derrière l’instruction / les appels dans la méthode. Également ajouté chemin de fichier existe vérifier

 using System;//AppDomain using System.Linq;//Where using System.Configuration;//app.config using System.Reflection;//BindingFlags using System.Io; ///  /// Use your own App.Config file instead of the default. ///  ///  public static void ChangeAppConfig(ssortingng NewAppConfigFullPathName) { if(File.Exists(NewAppConfigFullPathName) { AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", NewAppConfigFullPathName); ResetConfigMechanism(); return; } } ///  /// Remove cached values from ClientConfigPaths. /// Call this after changing path to App.Config. ///  private static void ResetConfigMechanism() { BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Static; /* s_initState holds one of the four internal configuration state. 0 - Not Started, 1 - Started, 2 - Usable, 3- Complete Setting to 0 indicates the configuration is not started, this will hint the AppDomain to reaload the most recent config file set thru .SetData call More [here][1] */ typeof(ConfigurationManager) .GetField("s_initState", Flags) .SetValue(null, 0); /*s_configSystem holds the configuration section, this needs to be set as null to enable reload*/ typeof(ConfigurationManager) .GetField("s_configSystem", Flags) .SetValue(null, null); /*s_current holds the cached configuration file path, this needs to be made null to fetch the latest file from the path provided */ typeof(ConfigurationManager) .Assembly.GetTypes() .Where(x => x.FullName == "System.Configuration.ClientConfigPaths") .First() .GetField("s_current", Flags) .SetValue(null, null); return; } 

Daniel, si possible, essayez d’utiliser d’autres mécanismes de configuration. Nous avons parcouru cette route où nous avions différents fichiers de configuration statiques / dynamics en fonction de l’environnement / du profil / du groupe et c’est devenu très compliqué à la fin.

Vous pouvez essayer une sorte de service Web de profil où vous spécifiez uniquement une URL de service Web à partir du client et, selon les détails du client (vous pouvez avoir des remplacements au niveau du groupe / utilisateur), il charge toutes les configurations nécessaires. Nous avons également utilisé MS Enterprise Library pour certaines parties.

c’était vous ne déployez pas de configuration avec votre client et vous pouvez le gérer séparément de vos clients