Have you ever needed to implement configuration sections for the application configuration file with C#? If you want to implement such a section for the application configuration file you need to dive deep into the System.Configuration namespace which offers quite powerful collections and attribute based configuration methods to implement configuration sections. But there is actually an easier way to implement such a section without divining deep into System.Configuration. The solution is serialization!
In this example I will use the DataContractSerializer but rest assured that the same goals could also be achieved with the XmlSerializer!
The basic trick behind the configuration section with the easy way is to implement the IConfigurationSectionHandler interface and mark the section implementation as a DataContract. Let’s see how this can be achieved.
[DataContract(Namespace = "")] public class MyConfigurationSectionHandler: IConfigurationSectionHandler { #region Implementation of IConfigurationSectionHandler public object Create(object parent, object configContext, XmlNode section) { } #endregion }
The basic structure of the configuration section handler is provided in the code example above. Your configuration section handler must implement IConfigurationSectionHandler and declare itself as DataContract with an empty namespace (code line 1). The namespace property must be set to an empty string, because this forces the DataContractSerializer to omit the namespace declaration in the serialized xml format and therefore allows the deserializing of xml files without a specified namespace.
Let’s say we want to achieve a configuration section like the following:
< ?xml version="1.0" encoding="utf-8" ?> <configuration> <configsections> <section name="myconfiguration" type="FullyQualifiedName.MyConfigurationSectionHandler, YourAssembly"/> </configsections> <myconfiguration> <language>German</language> <title>MyTitle</title> </myconfiguration> </configuration>
We only need to define two properties as data members on our custom section handler and an enumeration which will serve as input for our language property.
[DataContract] public enum Language { [EnumMember] German, [EnumMember] English, }
Important to note here is the fact, that the enumeration type must also be marked with the data contract attribute. If you don’t do this .NET will throw an exception when attempting to serialize/deserialize your custom section handler.
[DataContract(Namespace = "")] public class MyConfigurationSectionHandler: IConfigurationSectionHandler { #region Implementation of IConfigurationSectionHandler public object Create(object parent, object configContext, XmlNode section) { } #endregion [DataMember(Name = "title", Order = 2)] public string Title { get; private set; } [DataMember(Name = "language", Order = 1, IsRequired = false)] public ConfigurationLanguage Language { get; private set; } }
The DataMember Name property allows to specify the output name when serializing the custom configuration section. If no name is specified the DataContractSerializer assumes the name of the property (for example “Language”) as declaration. After declaring the data members the last thing we need to do is to implement the infrastructure code which deserializes the custom section. This can be easily achieved like the following:
public object Create(object parent, object configContext, XmlNode section) { DataContractSerializer xs = new DataContractSerializer(typeof(MyConfigurationSectionHandler)); XmlNodeReader xnr = new XmlNodeReader(section); try { var configSection = xs.ReadObject(xnr, false) as MyConfigurationSectionHandler; if (configSection != null) { Language = configSection.Language; Title= configSection.Title; } return this; } catch (Exception ex) { string s = ex.Message; Exception iex = ex.InnerException; while (iex != null) { s += "; " + iex.Message; iex = iex.InnerException; } throw new ConfigurationErrorsException( "Unable to deserialize an object of type \'" + GetType().FullName + "\' from the < " + section.Name + "> configuration section: " + s, ex, section); } }
The above code only uses the DataContractSerializer to deserialize the configuration section passed into the Create method. Then the deserialized section is used to assign the properties of the custom configuration section and a reference to this is passed to the caller. This trick can also be applied with configuration sections containing collections, dictionaries and even nested types as long as they are or can be marked serializable! Have fun with your own custom configuration sections!
Hi,
Can you please explain how can it works with collection?
the ConfigurationElementCollection is not marked as serializable so it not works for me.
Please help
Hi,
Sorry for the late response but I was in holiday. Coming back to your question. Please read the hints about Collection Types in Data Contracts for WCF. This should answer all your questions.
http://msdn.microsoft.com/en-us/library/aa347850.aspx
Daniel