In this post I will write something about the new section handler feature. I have written about the new way of creating section handler before. But this time I will focus on how to create a collection of adding child elements by using the ConfigurationElementCollection class. The code examples in this post will create a section handler that will get the following section automatically, without needing to use XmlDom to get the attributes or elements by our self:
<configSections>
<section name="group" type="Nsquared2.Web.Configuration.GroupSection, Nsquared2.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<group name="Group1">
<permission name="Read" description=""/>
<permission name="Write" description=""/>
<permission name="Modify" description=""/>
</group>
The new section handler uses classes where the root element in a section inherits the ConfigurationSection class and each child elements are represented by a classes that inherits the ConfigurationElelement class, and the element’s attributes are created as properties marked with the ConfigurationPropertyAttribute uses to map a property to an attribute. If the element has child controls like the group has in the example above, there is a special class created to add elements like the “permission” elements into a collection. Each collection is created as an own class that inherits the ConfigurationElementCollection class. The following example is the class for the “group” element where the “name” attribute is created as a property and where the “permission” elements are added to the PermissionElementCollection and can be collected from the Permissions property:
public class GroupSection : ConfigurationSection
{
[ConfigurationProperty("name", RequiredValue=true)]
public string Name
{
get { return (string)base["name"]; }
set { base["name"] = value; }
}
[ConfigurationProperty("permissions", DefaultCollectionProperty=true, RequiredValue=true)]
public PermissionElementCollection Permissions
{
get { return ((PermissionElementCollection)base["permissions"]); }
}
}
As you can see in the code above the Name property are marked with the ConfigurationPropetyAttribute where the “name” of the attribute is passed as an argument to the constructor and the attribute’s RequiredValue is set to true. The RequiredValue is used to specify that the attribute must be specified, if not an exception will be thrown. The Name property will add the mapped attribute’s value to the base class ConfigurationValues collection. The Permission property of the GroupSection class above will return a collection with PermissionElement objects. The DefaultCollectionProperty specify if the property represents the default collection of the “group” element. In this case we only have one collection and it’s the default collection. The “permissions” value passed to the ConfigurationPropertyAttribute’s constructor will not be an attribute or element that should be added to the “group” section in the configuration file. To get a value added to the “group” section, you can use the GetSection method of the ConfigurationSettings class:
GroupSection config = ConfigurationSettings.GetSection("group") as GroupSection;
When the GetSection method is executed, it will locate the “group” section in the <configSection> element and instantiate the specified type, and use reflection to find the specified ConfigurationProperties in the instantiated class, and automatically get elements and attributes values from the “group” section in the configuration file. The “permission” elements will also be collected and added to the PermissionElementCollection (I will soon show you the implementation of the collection).
Note: You don’t need to use a Create method as before when you implemented the IConfigurationSectionHandler, and you don’t need to use XmlDom to get the data from the XmlNode passed as an argument to the Create method.
Before I show you the implementation of the PermissionElementCollection, let’s take a look at the classes that will hold the information about the “permission” element and that will be added to the collection when the “group” section is requested from the GetSection method:
public class PermissionElement : ConfigurationElement
{
internal string _ElementName = "permission";
public PermissionElement()
{
}
protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
bool retVal = false;
if (!base.SerializeElement(null, false))
return false;
if (writer != null)
{
writer.WriteStartElement(this._ElementName);
retVal |= base.SerializeElement(writer, false);
writer.WriteEndElement();
return retVal;
}
return (retVal | base.SerializeElement(writer, false));
}
[ConfigurationProperty("name", RequiredValue=true)]
public string Name
{
get { return ((string)base["name"]); }
set { base["name"] = value; }
}
[ConfigurationProperty("description")]
public string Description
{
get { return ((string)base["description"]); }
set { base["description"] = value; }
}
}
The code above has two properties, Name and Description; both are mapped to its own attributes added to the “permission” element. The base class’s SerializeElement method most be overridden if we want to make it possible to add elements to the configuration file programmatically. As I wrote before, the GetSection method of the ConfiguartionSettings class will insatiate the PermissionElement class for each “permission” element added as child elements to the “group” section. Each PermissionElement will be added to the PermissionElementCollection. Let’s take a look at the implementation of the PermissionElementCollection class:
public class PermissionElementCollection : ConfigurationElementCollection
{
public PermissionGroupElement this[int index]
{
get
{
return (PermissionGroupElement)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
base.BaseRemoveAt(index);
this.BaseAdd(index, value);
}
}
protected override bool IsElementName(string elementName)
{
if ((string.IsNullOrEmpty(elementName)) || (elementName != "group"))
return false;
return true;
}
protected override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((PermissionGroupElement)element)._ElementName;
}
protected override ConfigurationElement CreateNewElement()
{
return new PermissionGroupElement();
}
}
When you create a collection class that inherits the ConfigurationElementCollection, the CollectionType property that decide which type of collection you are going to use, will by default only support <add>, <remove> and the <clear> element. To support your own elements, you have to override the CollectionType property and make sure to return the collection type BasicMap or BasicMapAlternate:
BasicMap
“Collections of this type contain elements that apply to the level at which they are specified, and to all child levels. A child level cannot modify the properties specified by a parent element of this type.” – MSDN Lib
BasicMapAlternate
“Same as BasicMap, except that this type causes the ConfigurationElementCollection object to sort its contents such that inherited elements are listed last.” – MSDN Lib
protected override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
When you use a BasicMap or BasicMapAlrernate collection type, you have to override the IsElementName and make sure to return true if the elemenetName argument of the IsElemntName is the name of your child element:
protected override bool IsElementName(string elementName)
{
if ((string.IsNullOrEmpty(elementName)) || (elementName != "permission"))
return false;
return true;
}
You also have to add an indexer to your collection class. If no indexer is created an exception will be thrown when you use the GetSection method to get the section.
public PermissionGroupElement this[int index]
{
get
{
return (PermissionGroupElement)base.BaseGet(index);
}
set
{
if (base.BaseGet(index) != null)
base.BaseRemoveAt(index);
this.BaseAdd(index, value);
}
}
The two last methods you most override are the GetElementKey and the CreateNewEelement. The GetElementKey will return the key of the child element and the CreateNewElement will return a new instance of the child element’s class:
protected override object GetElementKey(ConfigurationElement element)
{
return ((PermissionGroupElement)element).Name;
}
protected override ConfigurationElement CreateNewElement()
{
return new PermissionGroupElement();
}
I only wrote this class to give your some more information about the new section handler feature. You can find a lot of useful information in the VS 2005 MSDN Library.