23

He escrito una pequeña utilidad que me permite cambiar un AppSetting simple para App.config archivo de otra aplicación, y luego guardar los cambios:¿Puede ConfigurationManager conservar comentarios XML en Save()?

//save a backup copy first. 
var cfg = ConfigurationManager.OpenExeConfiguration(pathToExeFile); 
cfg.SaveAs(cfg.FilePath + "." + DateTime.Now.ToFileTime() + ".bak"); 

//reopen the original config again and update it. 
cfg = ConfigurationManager.OpenExeConfiguration(pathToExeFile); 
var setting = cfg.AppSettings.Settings[keyName]; 
setting.Value = newValue; 

//save the changed configuration. 
cfg.Save(ConfigurationSaveMode.Full); 

Esto funciona bien, excepto por un efecto secundario. El archivo .config recientemente guardado pierde todos los comentarios XML originales, pero solo dentro del área AppSettings. ¿Es posible conservar comentarios XML desde el área de configuración del archivo de configuración original?

Here's a pastebin of the full source if you'd like to quickly compile and run it.

+1

dieron el mismo problema aquí con .NET 4.0 – pipelinecache

+1

también descubrió que sólo lo hace en la sección appsettings. – pipelinecache

+0

Los comentarios se conservan en el resto de mi archivo .config también. Actualicé mi publicación para reflejar esto. –

Respuesta

26

Salté en Reflector.Net y miré la fuente decompilada para esta clase. La respuesta corta es no, no retendrá los comentarios. La forma en que Microsoft escribió la clase es para generar un documento XML a partir de las propiedades en la clase de configuración. Como los comentarios no aparecen en la clase de configuración, no vuelven a entrar en el XML.

Y lo que empeora esto es que Microsoft selló todas estas clases por lo que no puede derivar una nueva clase e insertar su propia implementación. Su única opción es mover los comentarios fuera de la sección AppSettings o utilizar las clases XmlDocument o XDocument para analizar los archivos de configuración en su lugar.

Lo siento. Este es un caso extremo que Microsoft no planeó.

2

Si los comentarios son críticos, que sólo podría ser que su única opción es leer & guardar el archivo manualmente (a través de XmlDocument o la nueva API LINQ relacionada). Sin embargo, si esos comentarios no son críticos, los dejaría ir o tal vez consideraría incluirlos como elementos de datos (aunque redundantes).

2

Aquí hay una función de muestra que puede usar para guardar los comentarios. Le permite editar un par de clave/valor a la vez. También he agregado algunas cosas para formatear el archivo muy bien en función de la forma en que uso habitualmente los archivos (puede eliminarlo fácilmente si lo desea). Espero que esto pueda ayudar a alguien más en el futuro.

public static bool setConfigValue(Configuration config, string key, string val, out string errorMsg) { 
    try { 
     errorMsg = null; 
     string filename = config.FilePath; 

     //Load the config file as an XDocument 
     XDocument document = XDocument.Load(filename, LoadOptions.PreserveWhitespace); 
     if(document.Root == null) { 
      errorMsg = "Document was null for XDocument load."; 
      return false; 
     } 
     XElement appSettings = document.Root.Element("appSettings"); 
     if(appSettings == null) { 
      appSettings = new XElement("appSettings"); 
      document.Root.Add(appSettings); 
     } 
     XElement appSetting = appSettings.Elements("add").FirstOrDefault(x => x.Attribute("key").Value == key); 
     if (appSetting == null) { 
      //Create the new appSetting 
      appSettings.Add(new XElement("add", new XAttribute("key", key), new XAttribute("value", val))); 
     } 
     else { 
      //Update the current appSetting 
      appSetting.Attribute("value").Value = val; 
     } 


     //Format the appSetting section 
     XNode lastElement = null; 
     foreach(var elm in appSettings.DescendantNodes()) { 
      if(elm.NodeType == System.Xml.XmlNodeType.Text) { 
       if(lastElement?.NodeType == System.Xml.XmlNodeType.Element && elm.NextNode?.NodeType == System.Xml.XmlNodeType.Comment) { 
        //Any time the last node was an element and the next is a comment add two new lines. 
        ((XText)elm).Value = "\n\n\t\t"; 
       } 
       else { 
        ((XText)elm).Value = "\n\t\t"; 
       } 
      } 
      lastElement = elm; 
     } 

     //Make sure the end tag for appSettings is on a new line. 
     var lastNode = appSettings.DescendantNodes().Last(); 
     if (lastNode.NodeType == System.Xml.XmlNodeType.Text) { 
      ((XText)lastNode).Value = "\n\t"; 
     } 
     else { 
      appSettings.Add(new XText("\n\t")); 
     } 

     //Save the changes to the config file. 
     document.Save(filename, SaveOptions.DisableFormatting); 
     return true; 
    } 
    catch (Exception ex) { 
     errorMsg = "There was an exception while trying to update the config value for '" + key + "' with value '" + val + "' : " + ex.ToString(); 
     return false; 
    } 
} 
+0

El código no se está compilando. Errores en esta línea: if (lastElement? .NodeType == System.Xml.XmlNodeType.Element && elm.NextNode? .NodeType == System.Xml.XmlNodeType.Comment) –

+0

The?. es un nuevo estilo de notación con VS2015 y .net 4.6. Simplemente hace una comprobación nula antes de obtener la propiedad. Puede reemplazarlo por: if ((lastElement! = Null && lastElement.NodeType == System.Xml.XmlNodeType.Element) && (elm.NextNode! = Null && elm.NextNode.NodeType == System.Xml.XmlNodeType .Comment)) –

+0

Gracias, volveré a verificar –

Cuestiones relacionadas