Managing Configuration Manager Part 1 – Module and Module Collection

So recently, I’ve been doing some coding that requires new entries in app.config. Well, let’s be honest, not ‘required’, but ‘it’d be cool’. And it was cool.

First of all, let me tell you the ‘requirements’.

  1. There are 63 modules that needs to be processed, divided into 9 unequal groups.
  2. Each module is located at different folders.
  3. Each module needs to be processed in order within its group, and each group needs to be processed in order as well. (Cool, no need for multi-threading whatsoever!)

So, here’s my initial thoughts: I’m going to have a Section of Groups, each containing one or more Modules. And maybe a SectionGroup to contain them all. Something like:

<groups>
  <group name="Group 1">
    <module order="1" name="SomeModule" path="Project1" />
    <module order="2" name="OtherModule" path="Project2" />
  </group>
  <group name="Group 2">
    <module order="1" name="YetAnotherModule" path="Project3" />
  </group>
</groups>

Then after trying to mangle with the Configuration Namespace, I realized that this is bigger than I thought. The Modules are an Element, I know that much. They have string properties and that’s it. The Groups has a string property, its name, and a ConfigurationProperty, a Collection of Modules. On top of that, Groups is a Collection of Groups, but itself doesn’t have any string properties.

I said, let’s start with the easiest one first. Let’s define the Module.

    public class Module : ConfigurationElement
    {
        private static ConfigurationProperty lBuildOrder;
        private static ConfigurationProperty sModulePath;
        private static ConfigurationProperty sModuleName;

        private static ConfigurationPropertyCollection oProperties;

        static Module()
        {
            lBuildOrder = new ConfigurationProperty(
                "order",
                typeof(long),
                long.Parse("0"),
                ConfigurationPropertyOptions.IsRequired
                );
            sModulePath = new ConfigurationProperty(
                "path",
                typeof(string),
                null
                );
            sModuleName = new ConfigurationProperty(
                "name",
                typeof(string),
                string.Empty,
                ConfigurationPropertyOptions.IsRequired
                );

            oProperties = new ConfigurationPropertyCollection();
            oProperties.Add(lBuildOrder);
            oProperties.Add(sModuleName);
            oProperties.Add(sModulePath);
        }

        [ConfigurationProperty("order", IsKey = true, IsRequired = true)]
        public long Order
        {
            get { return (long)base[lBuildOrder]; }
        }

        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return (string)base[sModuleName]; }
        }

        [ConfigurationProperty("path")]
        public string Path
        {
            get { return (string)base[sModulePath]; }
        }

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

As you can see, all I have here is the Order, Name, and Path properties. In keeping with C# convention, method names are capitalized. But XML properties are lowercase! So, I define each ConfigurationProperty with their lowercase names. The ConfigurationPropertyCollection isn’t strictly needed, but I was convinced that it’s a good practice to follow.

With this, a Module can be set as such:

    <module order="1" name="SomeModule" path="Project1" />
    <module order="2" name="OtherModule" path="Project2" />

Next, since the Groups will be containing a Collection of Modules, let’s continue with the ModuleCollection.

    [ConfigurationCollection(typeof(Module),
                             AddItemName = "module", 
                             CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class ModuleCollection : ConfigurationElementCollection
    {
        private static int currentOrder;

        static ModuleCollection()
        {
        }

        public ModuleCollection()
        {
        }

        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }

        protected override string ElementName
        {
            get { return "module"; }
        }

        public Module this[int index]
        {
            get { return (Module)base.BaseGet(index); }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                base.BaseAdd(index, value);
            }
        }

        public new Module this[string name]
        {
            get { return (Module)base.BaseGet(name); }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new Module();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as Module).Order;
        }
    }

Sweet, it’s a BasicMap collection. I define the “add” item to be “module” so I can use <module> instead of <add>. The two this methods allows me to later use a ModuleCollection object like any array, getting a value using the index position or the key, which in this case is the Module‘s name.

In the next part, I’ll continue with the Group and the GroupCollection classes.

Leave a Reply