Creating a Custom Configuration Section in Application Configuration
When you creating a framework is definitely you must create some custom configuration to handle your application configuration. .NET provide Namespace that we could manipulated or create custom Configuration, “System.Configuration”. For basic usage we could create element and element collection, and wrap it in a configuration section. You could do more advance by Grouping the configuration Section using Configuration Section Group.
Before creating custom configuration, first we must design the configuration file content first. I have a sample it using to inject implementation class to an interface class using reflection.
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="CustomMapping"
5: type="ConsoleApplication1.MappingSection, ConsoleApplication1"/>
6: </configSections>
7: <CustomMapping>
8: <MappingElements>
9: <MappingElement Interface ="IProduct" Implementation="ConsoleApplication1.Product" />
10: </MappingElements>
11: </CustomMapping>
12: </configuration>
In configuration file above we create a custom section name = “CustomMapping” and the type that act as a section class is MappingSection. We define a MappingElement for the basic configuration element and MappingElements for the configuration Element Collection. In the Configuration Element we define two attribute “Interface” and Implementation. That it is we ready to create our custom configuration code.
1. Creating Configuration Element by inheriting “System.Configuration.ConfigurationElement” class.
1: namespace ConsoleApplication1
2: {
3: using System.Configuration;
4:
5: public class MappingElement : ConfigurationElement
6: {
7: [ConfigurationProperty("Interface", IsKey = true, IsRequired = true)]
8: public string AttributeOne
9: {
10: get { return (string)this["Interface"]; }
11: set { this["Interface"] = value; }
12: }
13:
14: [ConfigurationProperty("Implementation", IsKey = true, IsRequired = true)]
15: public string AttributeTwo
16: {
17: get { return (string) this["Implementation"]; }
18: set { this["Implementation"] = value;}
19: }
20:
21: }
22: }
In this class we define a property that will exist in our configuration as an Attribute. Each property must state with .NET Attribute “System.Configuration.ConfigurationPropertyAttribute”. this class takes Name property for the identity | name of the attribute. and other optional property like whether the attribute is a key, must exist etc. When you have many of attribute that we want to specify you could use a class constant to declare all the name.
2. Creating Configuration Element Collection by inheriting “System.Configuration.ConfigurationElementCollection” class.
1: namespace ConsoleApplication1
2: {
3: using System.Configuration;
4:
5: public class MappingElementCollection : ConfigurationElementCollection
6: {
7:
8: protected override ConfigurationElement CreateNewElement()
9: {
10: return new MappingElement();
11: }
12:
13: protected override object GetElementKey(ConfigurationElement element)
14: {
15: return ((MappingElement)element).AttributeOne;
16: }
17:
18: protected override string ElementName
19: {
20: get
21: {
22: return "MappingElement";
23: }
24: }
25:
26: public override ConfigurationElementCollectionType CollectionType
27: {
28: get
29: {
30: return ConfigurationElementCollectionType.BasicMap;
31: }
32: }
33:
34: public MappingElement this[int index]
35: {
36: get { return this.BaseGet(index) as MappingElement; }
37: set {
38: if (this.BaseGet(index) != null)
39: {
40: this.BaseRemoveAt(index);
41: }
42: this.BaseAdd(index, value);
43: }
44: }
45:
46: public new MappingElement this[string interfaceShortName]
47: {
48: get { return this.BaseGet(interfaceShortName) as MappingElement; }
49: }
50: }
51:
52: }
This class will act as a collection to our Element. First we override CreateNewElement() method, this behavior is to set what element that will be create for this collection in this case we return our element class that have been create before. Next we override GetElementKey() method, this behavior is to retrieve what is the key for the element. This behavior is use when we retrieve element by it is using BaseGet() method. we also override ElementName is for setting the tag name of Element in XML configuration file. Overriding CollectionType to set the type we want to use, in this case we use BasicMap type. When use BasicType the child element child could not override property of its parent property.
The last two method is very important. This Indexer property is to manipulating element collection to retrieve child element by its index or by name. first Property is act like to add and retrieve collection by its index. and second is shadowing base class indexer and use name index to retrive specific element.
3. Finally we have to create section class that to handle Section for our custom configuration file. this could achieve by inheriting “System.Configuration.ConfigurationSection” class.
1: namespace ConsoleApplication1
2: {
3: using System.Configuration;
4:
5: public class MappingSection : ConfigurationSection
6: {
7: [ConfigurationProperty("MappingElements",IsDefaultCollection = true)]
8: public MappingElementCollection MappingElements
9: {
10: get { return (MappingElementCollection) base["MappingElements"]; }
11: }
12: }
13: }
Basically this class just wrap our element collection. so for simple usage we define a property that return our element collection type. define attribute ConfigurationProperty setting the Element Collection name, and set wheter is default collection or not.
That it is, we currently finished creating custom configuration file.
let see how to use our custom configuration file.
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="CustomMapping"
5: type="ConsoleApplication1.MappingSection, ConsoleApplication1"/>
6: </configSections>
7: <CustomMapping>
8: <MappingElements>
9: <MappingElement Interface ="IProduct" Implementation="ConsoleApplication1.Product" />
10: </MappingElements>
11: </CustomMapping>
12: </configuration>
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Configuration;
6:
7: namespace ConsoleApplication1
8: {
9: internal interface IProduct
10: {
11: string ProductName { get; set; }
12: }
13:
14: internal class Product : IProduct
15: {
16: private string _product;
17: #region IProduct Members
18:
19: public string ProductName
20: {
21: get
22: {
23: if (_product == "" || _product == null)
24: {
25: _product = "Pepsodent";
26: }
27: return _product;
28: }
29: set
30: {
31: _product = value;
32: }
33: }
34:
35: #endregion
36: }
37:
38: class Program
39: {
40: static void Main(string[] args)
41: {
42: MappingSection section = ConfigurationManager.GetSection("CustomMapping") as MappingSection;
43: Type type = Type.GetType(section.MappingElements[typeof (IProduct).Name].AttributeTwo);
44:
45: IProduct product = Activator.CreateInstance(type) as IProduct;
46: Console.WriteLine(product.ProductName);
47: }
48: }
49: }
First we get the section using System.Configuration.ConfigurationManager.GetSection() it takes the name of xml tag name in configuration in this case CustomMapping. when we have reference to section we could use property that we define in the class (MappingElements) that return element collection. we could use int indexer or name indexer to retrive element. in this case we use name indexer and retrieve property AttibuteTwo to retrive the implementation of the interface we want to create the instance (ConsoleApplication1.Product).
Happy Programming