在现有节点中添加嵌套组的App.config

11

我需要在根设置组中保存2个不同的设置组。应该像这样:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>    
    <sectionGroup name="ROOT_GROUP">
       <sectionGroup name="GROUP_1">
         ........................
         some_settings
         ........................
       </sectionGroup>
       <sectionGroup name="GROUP_2">
         ........................
         some_other_settings
         ........................
       </sectionGroup>
     </sectionGroup>
  </configSections>
................................
other_system_tags
................................
</configuration>

关键在于我需要在代码的不同位置逐个保存它们(例如,GROUP_1可能是连接字符串,GROUP_2是一些环境设置,它们都由用户在我的应用程序的不同部分填写)

我创建了这个简单的测试类来获得预期的结果

[TestFixture]
public class Tttt
{
    private string ROOT_GROUP = "ROOT_GROUP";
    private string GROUP_1 = "GROUP_1";
    private string GROUP_2 = "GROUP_2";

    [Test]
    public void SaveSettingsGroups()
    {
        SaveGroup1();
        SaveGroup2();
        Assert.True(true);
    }

    private Configuration GetConfig()
    {
        var configFilePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
        var map = new ExeConfigurationFileMap { ExeConfigFilename = configFilePath };
        var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        return config;
    }

    private void SaveGroup1()
    {
        var config = GetConfig();

        var root = new UserSettingsGroup();

        config.SectionGroups.Add(ROOT_GROUP, root);

        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(root.Name);

        var nested = new UserSettingsGroup();

        root.SectionGroups.Add(GROUP_1, nested);

        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(nested.Name);             
    }

    private void SaveGroup2()
    {
        var config = GetConfig();

        var root = config.GetSectionGroup(ROOT_GROUP);

        var nested = new UserSettingsGroup();
        root.SectionGroups.Add(GROUP_2, nested);

        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(nested.Name);
    }
}

但是由于某种原因,此代码的结果不同。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>    
    <sectionGroup name="ROOT_GROUP">
      <sectionGroup name="GROUP_1">
        ........................
        some_settings
        ........................
      </sectionGroup>
    </sectionGroup>
    <sectionGroup name="ROOT_GROUP">
      <sectionGroup name="GROUP_2">
      ........................
      some_other_settings
      ........................
      </sectionGroup>
    </sectionGroup>
  </configSections>
................................
other_system_tags
................................
</configuration>

ROOT_GROUP 节点被复制了,当我向已经存在的根组添加新的嵌套组并保存时,Visual Studio 抛出了一个异常,提示 ROOT_GROUP 已经存在。显然,我的问题隐藏在 SaveGroup2() 方法中 - 但是为什么呢?

更新: 我刚刚添加了一个新方法。

    private void SaveGroup3()
    {
        var config = GetConfig();

        var root = config.GetSectionGroup(ROOT_GROUP);
        var nested1 = root.SectionGroups.Get(0);

        var nested2 = new UserSettingsGroup();
        var nested3 = new UserSettingsGroup();

        nested1.SectionGroups.Add("GROUP_2", nested2);
        root.SectionGroups.Add("GROUP_3", nested3);
        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(nested2.Name);
        ConfigurationManager.RefreshSection(nested3.Name);
    }

替换它在测试中

[Test]
public void SaveSettingsGroups()
{
    SaveGroup1();
    SaveGroup3();
    Assert.True(true);
}

发现了这种奇怪的行为

  <sectionGroup name="ROOT_GROUP">
    <sectionGroup name="GROUP_1">
      <sectionGroup name="GROUP_2">
      </sectionGroup>
    </sectionGroup>
    <sectionGroup name="GROUP_3">
    </sectionGroup>
  </sectionGroup>

正如您所见,奇怪的地方在于结果完全符合预期。ROOT_GROUP并没有像我需要的那样重复,但为什么SaveGroup2()会出现这种情况呢?我在SaveGroup2()中漏掉了什么吗?

更新2 - 黑科技

只是尝试了一个简单的想法 - 如果在向其添加新嵌套元素之前清除root_group会发生什么?

    private void SaveGroup2()
    {
        var config = GetConfig();

        var root = config.GetSectionGroup(ROOT_GROUP);

        var nested = new ConfigurationSectionGroup();

        //Copy exiting nested groups to array
        var gr = new ConfigurationSectionGroup[5];       
        root.SectionGroups.CopyTo(gr,0);
        gr[1] = nested;
        //<!----

        root.SectionGroups.Clear();

        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(root.Name);

        root.SectionGroups.Add(gr[0].Name, gr[0]);
        root.SectionGroups.Add(GROUP_2, gr[1]);

        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection(root.Name);
    }

你可能会猜到,它是如何运作的!

<sectionGroup name="ROOT_GROUP">
  <sectionGroup name="GROUP_1" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
  </sectionGroup>
  <sectionGroup name="GROUP_2" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" >
  </sectionGroup>
</sectionGroup>

我觉得这看起来像一个错误,或者是有一些我错过的隐藏的东西。有人可以解释一下我做错了什么吗?


1
我不会通过告诉你简单的答案来赚取我的第100个赏金(我知道它只显示97)。避开ExeConfigurationFileMap类或任何更改配置的类,它们都很糟糕。配置文件只是XML。使用XML类,如LINQ to XML。 - Jeremy Thompson
这绝对是一个 bug。即使在你的 UPD 1 中,如果你的 Group_1 里面有东西,然后你再添加子 SectionGroup 到它里面,它会复制所有的根 Section。我建议你使用自定义配置,因为这个功能可能没有经过 Microsoft 的测试。 - Igor Tkachenko
@IgorTkachenko 谢谢您的回复,我决定在我的配置中放弃root_group。顺便说一句,程序员的世界非常令人兴奋,即使微软也受到其法则的约束。https://cs8.pikabu.ru/post_img/2016/06/23/9/1466695766198687223.jpg - whizzzkey
2个回答

1
我花了一段时间才弄清楚发生了什么,简而言之,对我来说,框架代码本身存在问题,特别是method WriteUnwrittenConfigDeclarationsRecursive(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) 在类MgmtConfigurationRecord中。我不想写太长的故事,但如果您愿意,可以调试 .Net 框架代码并自行查看。
你可以通过以下方式修复你的代码: 1. 将所有组保存在一起
private void SaveGroups()
{
    var config = GetConfig();
    var root = new ConfigurationSectionGroup();
    config.SectionGroups.Add(ROOT_GROUP, root);
    config.Save(ConfigurationSaveMode.Modified);
    ConfigurationManager.RefreshSection(root.Name);

    var nested = new UserSettingsGroup();
    root.SectionGroups.Add(GROUP_1, nested);

    nested = new UserSettingsGroup(); 
    root.SectionGroups.Add(GROUP_2, nested);

    config.Save(ConfigurationSaveMode.Modified);
    ConfigurationManager.RefreshSection(root.Name);
}

2. 在添加新项之前移除现有的组项

private void SaveGroup2()
{
    var config = GetConfig();
    var root = config.SectionGroups[ROOT_GROUP];
    var existingGroups = new Dictionary<string, ConfigurationSectionGroup>();
    while (root.SectionGroups.Count > 0)
    {
        existingGroups.Add(root.SectionGroups.Keys[0], root.SectionGroups[0]);
        root.SectionGroups.RemoveAt(0);
    }

    config.Save(ConfigurationSaveMode.Modified);

    existingGroups.Add(GROUP_2, new UserSettingsGroup());
    foreach (var key in existingGroups.Keys)
    {
        existingGroups[key].ForceDeclaration(true);
        root.SectionGroups.Add(key, existingGroups[key]);
    }

    config.Save(ConfigurationSaveMode.Modified);
    ConfigurationManager.RefreshSection(root.Name);
}

0
在您的第一次更新中,您在根目录下的第一个条目下添加了GROUP_2:
    //nested1 is now the first entry under root due to Get(0)
    var nested1 = root.SectionGroups.Get(0); 

    var nested2 = new UserSettingsGroup();
    var nested3 = new UserSettingsGroup();

    //I think you meant root here instead of nested1.
    nested1.SectionGroups.Add("GROUP_2", nested2);

你有尝试复现我的测试吗?你的回答毫无意义。 - whizzzkey
是的,当编码正确时,一切都按照预期工作。我不明白你在哪里遇到了理解上的问题。我上面的回答是我最好的尝试来帮助你理解。 - TheSoftwareJedi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接