ConfigurationManager可以在Save()上保留XML注释吗?

我写了一个小实用程序,允许我为另一个应用程序的App.config文件更改一个简单的AppSetting,然后保存更改:

//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); 

这种效果很好,除了一个副作用。 新保存的.config文件会丢失所有原始XML注释,但仅在AppSettings区域内。 是否可以从原始配置文件AppSettings区域保留XML注释?

如果您想快速编译并运行它,那么这是完整源代码的pastebin。

我跳进了Reflector.Net并查看了这个类的反编译源代码。 简短的回答是否定的,它不会保留评论。 Microsoft编写该类的方法是从配置类的属性生成XML文档。 由于注释没有出现在配置类中,因此它们不会将其返回到XML中。

更糟糕的是,Microsoft密封了所有这些类,因此您无法派生新类并插入自己的实现。 您唯一的选择是将注释移到AppSettings部分之外,或者使用XmlDocumentXDocument类来解析配置文件。

抱歉。 这是微软刚刚没有计划的边缘案例。

如果注释很关键,那么可能只是您的唯一选择是手动读取和保存文件(通过XmlDocument或新的Linq相关API)。 然而,如果这些评论并不重要,我会让他们离开或考虑将它们嵌入(尽管是多余的)数据元素。

以下是可用于保存注释的示例函数。 它允许您一次编辑一个键/值对。 我还添加了一些东西来根据我常用文件的方式很好地格式化文件(你可以轻松删除它,如果你想)。 我希望将来可以帮助其他人。

 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; } }