Managing Configuration Manager Part 3 – Section and SectionGroup

So far, all of our classes are either a ConfigurationElement or a Collection of it. We can’t use Elements in a configuration as is. They need to be contained in a Section.

Enter ProcessGroup. This is our first Section. It contains an array of Groups in a neat GroupCollection and no string properties at all.

    public class ProcessGroup : ConfigurationSection
    {
        private static ConfigurationProperty oGroupCollection;

        private static ConfigurationPropertyCollection oProperties;

        private static ProcessGroup thisSection;

        public ProcessGroup()
        {
            oGroupCollection = new ConfigurationProperty(
                "groups",
                typeof(GroupCollection),
                null,
                ConfigurationPropertyOptions.IsRequired
                );
            oProperties = new ConfigurationPropertyCollection();

            oProperties.Add(oGroupCollection);
        }

        [ConfigurationProperty("groups", IsRequired = true)]
        [ConfigurationCollection(typeof(Group), AddItemName = "group")]
        public GroupCollection Groups
        {
            get { return (GroupCollection)base[oGroupCollection]; }
        }

        public static ProcessGroup GetSection()
        {
            return GetSection("processGroups");
        }

        public static ProcessGroup GetSection(string definedName)
        {
            if (thisSection == null)
            {
                thisSection = ConfigurationManager.GetSection(definedName) as ProcessGroup;
                if (thisSection == null)
                {
                    throw new ConfigurationErrorsException("The <" + definedName + "> section is not defined in your .config file!");
                }
            }

            return thisSection;
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return oProperties; }
        }
    }

The only difference here from Group, aside from this extending from ConfigurationSection, is the GetSection methods. These methods make it easy to get the Section from app.config. It also applies a simple Singleton pattern by keeping a reference of the Section in memory when it finds it. For huge Sections, this could be a performance bonus.

Now, my original ‘requirements’ only calls for a single Section, but I’d rather have the foundation ready before I need it. So let’s add a SectionGroup.

    public class ProcessSettings : ConfigurationSectionGroup
    {
        public ProcessSettings() { }

        public ProcessGroup ProcessGroups()
        {
            return ProcessGroup.GetSection();
        }
    }

Tada! That’s it. The entire SectionGroup only has one method to call the only Section in this SectionGroup. When I later add new Section, all I need to add is a method to get them in this class.

For the app.config itself, we need to add entries to the <configSections> so the XML is strongly typed.

  <configSections>
    <sectionGroup name="processSettings"
                  type="ModuleProcessor.Configuration.ProcessSettings, ModuleProcessor">
      <section name="processGroups"
               type="ModuleProcessor.Configuration.LinkGroup, ModuleProcessor"/>
    </sectionGroup>
  </configSections>

Furthermore, the actual configuration now becomes:

  <processSettings>
    <processGroups>
      <groups>
        <group name="Group 1">
          <modules order="1" name="SomeModules" path="Project1" />
          <modules order="2" name="OtherModule" path="Project2" />
        </group>
        <group name="Group 2">
          <modules order="3" name="YetAnotherModule" path="OtherProject" />
        </group>
      </groups>
    </processGroups>
  </processSettings>

Now, to use this in actual code, you’ll need help from ConfigurationManager

  Configuration Config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
  ProcessSettings Settings = (ProcessSettings)Config.GetSectionGroup("processSettings");
  ProcessGroup GroupSection = Settings.Sections["processGroups"];
  Group[] Groups = GroupSection.Groups;
  Module[] Modules = Groups[0].Modules;
  
  string NameOfModuleOneGroupOne = Modules[0].Name;                        // "SomeModule"
  string NameOfModuleTwoGroupOne = GroupSection.Groups[0].Modules[1].Name; // "OtherModule"
  string PathOfModuleOneGroupTwo = GroupSection.Groups[1].Modules[0].Path; // "OtherProject"

Leave a Reply