I have to save 2 different groups of settings in my root settings group. It should looks like this:
<?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>
The Nuance is that I have to save it one after another in different places in my code. (For example, GROUP_1 can be a connection strings and GROUP_2 is some environment settings and they both together are filling by users in different sections of my application)
I made this simple test class to get the expected result
[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);
}
}
BUT for some reason the result of this code is different
<?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>
The ROOT_GROUP node is duplicated and of course visual studio throws me an exception that ROOT_GROUP is already exists. Obviously, my problem is hidden in method SaveGroup2() when I add new nested group to existed root group and then save it - but why?
UPD I've just added new method
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);
}
And replace it in test
[Test]
public void SaveSettingsGroups()
{
SaveGroup1();
SaveGroup3();
Assert.True(true);
}
And got this strange behaviour
<sectionGroup name="ROOT_GROUP">
<sectionGroup name="GROUP_1">
<sectionGroup name="GROUP_2">
</sectionGroup>
</sectionGroup>
<sectionGroup name="GROUP_3">
</sectionGroup>
</sectionGroup>
As you can see, the strangeness is in that the result is totally expected. ROOT_GROUP wasn't duplicate, as I needed it, but why it does in SaveGroup2()? Did I miss something in SaveGroup2()?
UPD2 - HACK
Just tried a simple idea - what if I would clear the root_group before adding a new nested element to it?
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);
}
And how do you probably guess - it works!
<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>
I think it looks like a bug or there are some hidden things that I missed. Can somebody explain me what did I do wrong?
It took me a while to figure-out what was going on and tl;dr it seems, to me, there is a problem with the framework code itself, particularly method WriteUnwrittenConfigDeclarationsRecursive(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent)
inside class MgmtConfigurationRecord
. I don't want to write a long story but if you wish you could debug .Net framework code and see for yourself.
You can fix your code in following ways:
1. Save all groups together
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. Remove existing group items before adding a new one
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With