Search code examples
c#app-configconfigurationmanager

Is there a way to read configuration sections without knowing the name of the sections? (And some more)


I'm working with a program, that needs to deal with some so-called "interfaces". The names of those "interfaces" is not known by the program, but the program needs to be able to read the names of the interfaces, the names and the values of the fields as provided in the configuration file.

In order to do this, I've created an "App.config" file as follows:

<configuration>
    <configSections>
        <sectionGroup name="interfaces">
            <section name="IF1" type="System.Configuration.AppSettingsSection"/>
            <section name="IF2" type="System.Configuration.AppSettingsSection"/>
            <section name="IF3" type="System.Configuration.AppSettingsSection"/>
        </sectionGroup>
    </configSections>
    <interfaces>
        <IF1>
            <add key="IF1.ENABLE" value="false"/>
        </IF1>
        <IF2>
            <add key="IF2.ENABLE" value="true"/>
            <add key="IF2.ENABLE_EVENTS" value="true"/>
            <add key="IF2.MAX" value="1000"/>
            <add key="IF2.MIN" value="100"/>
        </IF2>
        ...

However, in order to read that information in the configuration file, I'm using the following code (with the corresponding problem):

NameValueCollection settings = ConfigurationManager.GetSection("interfaces")   as NameValueCollection;
  // returns "null"
NameValueCollection settings = ConfigurationManager.GetSection("interfaces/*") as NameValueCollection;
  // returns "null"
NameValueCollection settings = ConfigurationManager.GetSection("interfaces/IF1") as NameValueCollection;
  // works OK, but it means that the name of the interface needs to be known by the application.

While checking the settings (from the last line) in the watch-window, this is the way I found for viewing the value of the first entry using Add watch (please don't laugh):

new System.Collections.ArrayList.ArrayListDebugView(
  ((System.Collections.Specialized.NameObjectCollectionBase.NameObjectEntry)
   (new System.Collections.Hashtable.HashtableDebugView(settings._entriesTable).Items[0]).Value).Value).Items[0]

Trying to see this does not work, for the simple reason that settings._entriesTable is private, so it's not accessible.

Does anybody know a simple way to say: (pseudo-code)

foreach (entry) in configuration.interfaces
{
  foreach (interface_entry) in configuration.interfaces.getSection(entry.name)
  {
    string keyname   = interface_entry.key;
    string valuename = interface_entry.value;
  }
}

Or did I do something entirely wrong in the format of my configuration file?


Solution

  • I've tried to use the code in this proposed duplicate, but I've needed to do some modifications:

    private static List<string> GetNames(ConfigurationSectionGroup configSectionGroup)
    {
        var names = new List<string>();
        foreach (ConfigurationSectionGroup csg in configSectionGroup.SectionGroups)
            names.AddRange(GetNames(csg));
    
        foreach (ConfigurationSection cs in configSectionGroup.Sections)
            if (configSectionGroup.SectionGroupName == "interfaces")
            names.Add(cs.SectionInformation.SectionName);
    
        return names;
    }
    

    Apparently, the original code returns entries like interfaces/interfaces/IF1, while I just want interfaces/IF1. Putting the configSectionGroup.SectionGroupName in comment solves this issue.

    The main function originally contained two sections:

    /* Section 1 */
    foreach (ConfigurationSectionGroup csg in config.SectionGroups)
      names.AddRange(GetNames(csg));
             
    /* Section 2 */
    foreach (ConfigurationSection cs in config.Sections)
      names.Add(cs.SectionInformation.SectionName);
    

    I have no idea what "Section 2" exactly does (where it gets its information from), but I have removed it from my code and now it's working fine.