In order to create a custom config section handler we would use the following namespaces:
System.Configuration
System.Collections
System.Collections.Generic
System.Collections.Specialized
Now a sample xml for which we intend to create a config section handler:
<TransformerConfigurationSection MaxSlotsPerMachine="5">
<!-- Remote Service Machines-->
<servicemachine id="01">
<Id value="1" />
<Name value="server1" />
</servicemachine>
<servicemachine id="02">
<Id value="2" />
<Name value="server2" />
</servicemachine>
</TransformerConfigurationSection>
Now first we create a ConfigurationSection Class that reads this section from a config file. The code looks as follows:
Now we create the class MachineElementCollection that represents a ConfigurationElementCollection.
That is it. The config section reader is done. Now just add the above xml to your app.config or web.config and define the configsection as shown below:
public class TransformerConfigurationSection : ConfigurationSection
{
// 1. MaxSlotsPerMachine -- attribute
// 2. serviceMachine -- Element collection
public const string SectionXPath = "TransformerConfigurationSection";
public TransformerConfigurationSection()
{
Properties.Add(new ConfigurationProperty("MaxSlotsPerMachine", typeof(string), null));
}
public static TransformerConfigurationSection GetSection()
{
return (TransformerConfigurationSection)ConfigurationManager.GetSection(SectionXPath);
}
[ConfigurationProperty("MaxSlotsPerMachine")]
public string MaxSlotsPerMachine
{
get
{
return (string)this["MaxSlotsPerMachine"];
}
}
[ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)]
public MachineElementCollection ServiceMachines
{
get
{
return (MachineElementCollection)this[""];
}
}
}
Now we create the class MachineElementCollection that represents a ConfigurationElementCollection.
public class MachineElementCollection : MachineConfigurationElementCollection<ServiceMachineElement>
{
public MachineElementCollection()
:base("servicemachine")
{
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ServiceMachineElement) element).Id;
}
}
Now create a base class that allows you to reuse the ConfigurationElementCollection to be of any type.
public abstract class MachineConfigurationElementCollection<T> : ConfigurationElementCollection where T: ConfigurationElement, new()
{
private ConfigurationElementCollectionType collectionType;
private string elementName;
public override ConfigurationElementCollectionType CollectionType
{
get
{
return collectionType;
}
}
protected override string ElementName
{
get
{
return String.IsNullOrEmpty(elementName) ? base.ElementName : elementName;
}
}
public virtual T this[object key]
{
get
{
var configurationElementType = (T)BaseGet(key);
return (object)configurationElementType != null ? configurationElementType : null;
}
set
{
if (IsReadOnly())
Add(value);
if (GetElementKey(value).ToString().Equals((string)key, StringComparison.Ordinal))
{
if (BaseGet(key) != null)
BaseRemove(key);
Add(value);
}
}
}
public T this[int index]
{
get
{
return (T)BaseGet(index);
}
set
{
if (!IsReadOnly() && !ThrowOnDuplicate && BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
internal MachineConfigurationElementCollection()
: this(ConfigurationElementCollectionType.AddRemoveClearMap, null)
{
}
internal MachineConfigurationElementCollection(string elementname)
: this(ConfigurationElementCollectionType.AddRemoveClearMap, elementname)
{
}
internal MachineConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName)
{
this.collectionType = collectionType;
this.elementName = elementName;
if (string.IsNullOrEmpty(elementName))
return;
AddElementName = elementName;
}
internal MachineConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName, IComparer comparer)
: base(comparer)
{
this.collectionType = collectionType;
this.elementName = elementName;
}
protected override void BaseAdd(ConfigurationElement element)
{
if (!IsReadOnly() && !ThrowOnDuplicate)
{
object elementKey = this.GetElementKey(element);
if (ContainsKey(elementKey))
BaseRemove(elementKey);
}
base.BaseAdd(element);
}
public void Add(T element)
{
BaseAdd(element);
}
public void Clear()
{
BaseClear();
}
public virtual bool ContainsKey(object key)
{
if (key != null)
return null != BaseGet(key);
var list = new List<string>();
foreach (PropertyInformation propertyInformation in (NameObjectCollectionBase) CreateNewElement().ElementInformation.Properties)
{
if (propertyInformation.IsKey)
list.Add(propertyInformation.Name);
}
return false;
}
protected override ConfigurationElement CreateNewElement()
{
return Activator.CreateInstance<T>();
}
}
Now since the ConfigurationElementCollection has been created now we create the actual ConfigurationElement.
public class ServiceMachineElement : ConfigurationElement
{
public ServiceMachineElement()
{
Properties.Add(new ConfigurationProperty(ServiceMachineIdKey, typeof(string), ""));
Properties.Add(new ConfigurationProperty(IdKey, typeof(IdElement), null));
Properties.Add(new ConfigurationProperty(NameKey, typeof(NameElement), null));
}
private const string ServiceMachineIdKey = "id";
[ConfigurationProperty(ServiceMachineIdKey, IsRequired = false)]
public string ServiceMachineId
{
get { return (String)this[ServiceMachineIdKey]; }
set { this[ServiceMachineIdKey] = value; }
}
private const string IdKey = "Id";
[ConfigurationProperty(IdKey, IsRequired = true)]
public IdElement Id
{
get { return (IdElement)this[IdKey]; }
set { this[IdKey] = value; }
}
private const string NameKey = "Name";
[ConfigurationProperty(NameKey, IsRequired = true)]
public NameElement Name
{
get { return (NameElement)this[NameKey]; }
set { this[NameKey] = value; }
}
}
Since, we have the servicemachine element with child elements of type NameElement and IdElement we create these classes.
public class IdElement : ConfigurationElement
{
public IdElement()
{
Properties.Add(new ConfigurationProperty(ValueKey, typeof(string), null));
}
private const string ValueKey = "value";
[ConfigurationProperty(ValueKey, IsRequired = true)]
public string Value
{
get { return (string)this[ValueKey]; }
set { this[ValueKey] = value; }
}
}
public class NameElement : ConfigurationElement
{
public NameElement()
{
Properties.Add(new ConfigurationProperty(ValueKey, typeof(string), null));
}
private const string ValueKey = "value";
[ConfigurationProperty(ValueKey, IsRequired = true)]
public string Value
{
get { return (string)this[ValueKey]; }
set { this[ValueKey] = value; }
}
}
That is it. The config section reader is done. Now just add the above xml to your app.config or web.config and define the configsection as shown below:
<configuration>
<configSections>
<section name="TransformerConfigurationSection" type="Sample.TransformerConfigurationSection, Sample"/>
</configSections>
</configuration>