Cambiar app.config predeterminado en el tiempo de ejecución

Tengo el siguiente problema:
Tenemos una aplicación que carga módulos (complementos). Estos módulos pueden necesitar entradas en la aplicación.config (por ejemplo, configuración WCF). Debido a que los módulos se cargan dinámicamente, no quiero tener estas entradas en el archivo app.config de mi aplicación.
Lo que me gustaría hacer es lo siguiente:

  • Cree una nueva app.config en la memoria que incorpore las secciones de configuración de los módulos
  • Dile a mi aplicación que use esa nueva app.config

Nota: ¡No quiero sobrescribir la aplicación predeterminada.config!

Debería funcionar de forma transparente, de modo que, por ejemplo, ConfigurationManager.AppSettings use ese nuevo archivo.

Durante mi evaluación de este problema, se me ocurrió la misma solución que aquí: recarga app.config con nunit .
Desafortunadamente, no parece hacer nada, porque todavía obtengo los datos de la aplicación normal .config.

Usé este código para probarlo:

 Console.WriteLine(ConfigurationManager.AppSettings["SettingA"]); Console.WriteLine(Settings.Default.Setting); var combinedConfig = string.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); } 

Imprime los mismos valores twices, aunque combineConfig contiene otros valores que el app.config normal.

El truco en la pregunta vinculada funciona si se usa antes de que se use el sistema de configuración la primera vez. Después de eso, ya no funciona.
La razón:
Existe una clase ClientConfigPaths que almacena en caché las rutas. Por lo tanto, incluso después de cambiar la ruta de acceso con SetData , no se vuelve a leer, porque ya existen valores almacenados en caché. La solución es eliminar esto también:

 using System; using System.Configuration; using System.Linq; using System.Reflection; public abstract class AppConfig : IDisposable { public static AppConfig Change(string path) { return new ChangeAppConfig(path); } public abstract void Dispose(); private class ChangeAppConfig : AppConfig { private readonly string oldConfig = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToString(); private bool disposedValue; public ChangeAppConfig(string 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); } } } 

El uso es así:

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

Si desea cambiar el archivo app.config utilizado durante todo el tiempo de ejecución de su aplicación, simplemente coloque AppConfig.Change(tempFileName) sin utilizar en algún lugar al inicio de su aplicación.

Puede intentar usar Configuration y Agregar ConfigurationSection en tiempo de ejecución

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

EDITAR: Aquí hay una solución basada en la reflexión (aunque no muy bonita)

Crear clase derivada de IInternalConfigSystem

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

luego, a través de la reflexión, configúralo en un campo privado en 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 solución @Daniel funciona bien. Una solución similar con más explicaciones está en la esquina c-sharp. Para completar, me gustaría compartir mi versión: con el using y las banderas de bits abreviadas.

 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(string 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 solución de Daniel parece funcionar incluso para ensamblajes posteriores que había usado AppDomain.SetData antes, pero desconocía cómo restablecer los indicadores de configuración interna

Convertido a C ++ / CLI para aquellos interesados

 ///  /// 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(String ^NewAppConfigFullPathName) { AppDomain::CurrentDomain->SetData(L"APP_CONFIG_FILE", NewAppConfigFullPathName); ResetConfigMechanism(); return; } 

Si alguien está interesado, aquí hay un método que funciona en Mono.

 string 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 su archivo de configuración se acaba de escribir con clave / valores en “appSettings”, entonces puede leer otro archivo con dicho código:

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

Luego puede leer la sección. Configuración como colección de KeyValueConfigurationElement.

Discusión maravillosa, agregué más comentarios al método ResetConfigMechanism para comprender la magia detrás de las declaraciones / llamadas en el método. También se agregó la ruta del archivo existente

 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(string 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 es posible intenta usar otros mecanismos de configuración. Hemos pasado por esta ruta en la que teníamos diferentes archivos de configuración estáticos / dynamics según el entorno / perfil / grupo y al final se volvieron bastante complicados.

podría probar algún tipo de servicio web de perfil donde solo especifique una URL de servicio web del cliente y dependiendo de los detalles del cliente (puede tener anulaciones de nivel de grupo / usuario), carga todas las configuraciones que necesita. También hemos utilizado MS Enterprise Library para una parte de ella.

es decir, no implementas las configuraciones con tu cliente y puedes administrarlas separadamente de tus clientes