2012-07-30 15 views
5

¿Hay alguna manera de cargar la configuración de un archivo diferente que no sea el archivo predeterminado App.config en tiempo de ejecución? Me gustaría hacer esto después de que se cargue el archivo de configuración predeterminado.Cargando Properties.Settings de un archivo diferente en tiempo de ejecución

Uso la interfaz gráfica de usuario Settings.Settings en Visual Studio para crear mi archivo App.config para mí. El archivo de configuración termina pareciéndose a esto:

<?xml version="1.0" encoding="utf-8" ?> 
    <configuration> 
     <configSections> 
      <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > 
     <section name="SnipetTester.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 
    </sectionGroup> 
    </configSections> 
     <applicationSettings> 
     <SnipetTester.Properties.Settings> 
      <setting name="SettingSomething" serializeAs="String"> 
      <value>1234</value> 
      </setting> 
     </SnipetTester.Properties.Settings> 
     </applicationSettings> 
    </configuration> 

En el código, que soy capaz de acceder a la configuración de la siguiente manera:

Console.WriteLine("Default setting value: " + Properties.Settings.Default.SettingSomething); 

La idea es que cuando se ejecuta la aplicación, que debería estar capaz de especificar un archivo de configuración en tiempo de ejecución y hacer que la aplicación cargue el archivo de configuración en el objeto Properties.Settings.Default en lugar de usar el archivo predeterminado app.config. Los formatos de los archivos de configuración serían los mismos, pero los valores de la configuración serían diferentes.

Yo sé de una manera de hacer esto con el ConfigurationManager.OpenExeConfiguration(configFile);. Sin embargo, en las pruebas que he ejecutado, no actualiza el objeto Properties.Settings.Default para reflejar los nuevos valores del archivo de configuración.


Después de pensar en esto un poco más de tiempo, he podido encontrar una solución que me gusta un poco mejor. Estoy seguro de que tiene algunos inconvenientes, pero creo que funcionará para lo que necesito que haga.

Esencialmente, la clase Properties.Settings se genera automáticamente por Visual Studio; genera el código para la clase para ti. Pude encontrar dónde se generó el código y agregar algunas llamadas a funciones para cargar un archivo de configuración por sí mismo. Aquí está mi además:

internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 
{ 
    //Parses a config file and loads its settings 
    public void Load(string filename) 
    { 
     System.Xml.Linq.XElement xml = null; 
     try 
     { 
      string text = System.IO.File.ReadAllText(filename); 
      xml = System.Xml.Linq.XElement.Parse(text); 
     } 
     catch 
     { 
      //Pokemon catch statement (gotta catch 'em all) 

      //If some exception occurs while loading the file, 
      //assume either the file was unable to be read or 
      //the config file is not in the right format. 
      //The xml variable will be null and none of the 
      //settings will be loaded. 
     } 

     if(xml != null) 
     { 
      foreach(System.Xml.Linq.XElement currentElement in xml.Elements()) 
      { 
       switch (currentElement.Name.LocalName) 
       { 
        case "userSettings": 
        case "applicationSettings": 
         foreach (System.Xml.Linq.XElement settingNamespace in currentElement.Elements()) 
         { 
          if (settingNamespace.Name.LocalName == "SnipetTester.Properties.Settings") 
          { 
           foreach (System.Xml.Linq.XElement setting in settingNamespace.Elements()) 
           { 
            LoadSetting(setting); 
           } 
          } 
         } 
         break; 
        default: 
         break; 
       } 
      } 
     } 
    } 

    //Loads a setting based on it's xml representation in the config file 
    private void LoadSetting(System.Xml.Linq.XElement setting) 
    { 
     string name = null, type = null, value = null; 

     if (setting.Name.LocalName == "setting") 
     { 
      System.Xml.Linq.XAttribute xName = setting.Attribute("name"); 
      if (xName != null) 
      { 
       name = xName.Value; 
      } 

      System.Xml.Linq.XAttribute xSerialize = setting.Attribute("serializeAs"); 
      if (xSerialize != null) 
      { 
       type = xSerialize.Value; 
      } 

      System.Xml.Linq.XElement xValue = setting.Element("value"); 
      if (xValue != null) 
      { 
       value = xValue.Value; 
      } 
     } 


     if (string.IsNullOrEmpty(name) == false && 
      string.IsNullOrEmpty(type) == false && 
      string.IsNullOrEmpty(value) == false) 
     { 
      switch (name) 
      { 
       //One of the pitfalls is that everytime you add a new 
       //setting to the config file, you will need to add another 
       //case to the switch statement. 
       case "SettingSomething": 
        this[name] = value; 
        break; 
       default: 
        break; 
      } 
     } 
    } 
} 

El código añadí expone una función Properties.Settings.Load(string filename). La función acepta un nombre de archivo config como parámetro. Analizará el archivo y cargará cualquier configuración que encuentre en el archivo de configuración. Para volver a la configuración original, simplemente llame al Properties.Settings.Reload().

¡Espero que esto ayude a otra persona!

+0

esto parece muy prometedor, pero ¿cómo podemos utilizar los tipos – tofutim

Respuesta

0

buscar en el uso ExeConfigurationFileMap y ConfigurationManager.OpenMappedExeConfiguration.

Ver Cracking the Mysteries of .Net 2.0 Configuration

El ExeConfigurationFileMap le permite configurar específicamente los nombres de las rutas exactas a máquina, exe, roaming y archivos de configuración locales , todos juntos, o poco a poco, al llamar OpenMappedExeConfiguration(). No es necesario que especifique todos los archivos , pero todos los archivos se identificarán y fusionarán cuando se cree el objeto de configuración . Al utilizar OpenMappedExeConfiguration, es importante comprender que todos los niveles de configuración de hasta el nivel que solicite se fusionarán siempre . Si especifica un archivo de configuración local y un archivo ejecutable personalizado, , pero no especifica una máquina ni un archivo móvil, la máquina predeterminada y los archivos itinerantes se encontrarán y fusionarán con los archivos exe y user especificados. Esto puede tener consecuencias inesperadas si los archivos especificados no se han mantenido correctamente sincronizados con los archivos predeterminados.

1

Depende del tipo de la aplicación:

  1. aplicación Web & de aplicaciones de Windows - utilizar el atributo configSource xml si está dispuesto para almacenar los archivos de configuración en la misma carpeta (o subcarpetas) como la aplicación
  2. Crea un settings provider y también implementa IApplicationSettingsProvider. Muestras here y here. Es posible que también deba utilizar la interfaz IConfigurationManagerInternal para reemplazar el administrador de configuración .NET predeterminado. Al implementar el proveedor, no olvide marcar la diferencia entre la configuración del usuario y la configuración de la aplicación y los perfiles móviles.

Si desea empezar a trabajar rápidamente simplemente descompilar la clase LocalFileSettingsProvider (el proveedor de configuración por defecto) y el cambio a sus necesidades (que puede encontrar algo de código useles y puede ser que necesite para replicar todas las clases de las que depende)

Buena suerte

0

Puede incluir los tipos por lo que no tiene que actualizar manualmente la fuente cada vez.

`vacío privado LoadSetting (configuración System.Xml.Linq.XElement) { string name = null, type = null; valor de cadena = nulo;

 if (setting.Name.LocalName == "setting") 
     { 
      System.Xml.Linq.XAttribute xName = setting.Attribute("name"); 
      if (xName != null) 
      { 
       name = xName.Value; 
      } 

      System.Xml.Linq.XAttribute xSerialize = setting.Attribute("serializeAs"); 
      if (xSerialize != null) 
      { 
       type = xSerialize.Value; 
      } 

      System.Xml.Linq.XElement xValue = setting.Element("value"); 
      if (xValue != null) 
      { 
       if (this[name].GetType() == typeof(System.Collections.Specialized.StringCollection)) 
       { 
        foreach (string s in xValue.Element("ArrayOfString").Elements()) 
        { 
         if (!((System.Collections.Specialized.StringCollection)this[name]).Contains(s)) 
          ((System.Collections.Specialized.StringCollection)this[name]).Add(s); 
        } 
       } 
       else 
       { 
        value = xValue.Value; 
       } 

       if (this[name].GetType() == typeof(int)) 
       { 
        this[name] = int.Parse(value); 
       } 
       else if (this[name].GetType() == typeof(bool)) 
       { 
        this[name] = bool.Parse(value); 
       } 
       else 
       { 
        this[name] = value; 
       } 

      } 
     }` 
+0

es posible manejar esto por tipo arbitrario – tofutim

Cuestiones relacionadas