When we talk about configuration file in .NET, the obvious first question, which pops up in our mind is what is a configuration file and why do we actually need it?
According to MSDN, the Configuration files are the XML files that can be changed as needed.The reason why we actually need them is that we can change some data that may be required by the Application without recompiling the Application again.
While working with the .NET application we come across two very common configuration files ie. app.config and web.config. Both these configuration files have a <appSetting/> tag, where we can add the settings related to an application.But appSetting tag allows us to add only key value pair.
- <appSettings>
- <add key="" value=""/>
- </appSettings>
But there are many scenarios where we want our settings to be something more than just key value pairs.
To overcome this situation, Microsoft provides many classes, which allows us to write our custom configuration section. All the classes related to these are present in System.Configuration dll.In this sample Application, I will show how to create your custom configuration section Group, configuration section, configuration element. In the later part of this sample, I will be showing how to add intellisence to our custom tags in Visual Studio.
First of all, we will create a new class library project and name it as Custom tags.
Now, we will add the reference to System.Configuration to the project.
First of all, we will create a class that represents the innermost element of the custom section. In our case, it is company tag. To create a configuration element, we need to inherit it from ConfigurationElement class. Now, we need to define the attributes of the configuration element. For this, we will create public properties and add a ConfigurationPropertyAttribute to it. This ConfigurationProperty attribute has various parameters like name, DefaultValue, IsKey, IsRequired. The screenshot is given below of the CompanyElement class.
- #region Company Element
- public class CompanyElement : ConfigurationElement
- {
- [ConfigurationProperty("name", DefaultValue = "", IsKey = true, IsRequired = true)]
- public string Name
- {
- get { return (string)this["name"]; }
- set { this["name"] = value; }
- }
-
- [ConfigurationProperty("shortName", DefaultValue = "", IsRequired = true)]
- public string ShortName
- {
- get { return (string)this["shortName"]; }
- set { this["shortName"] = value; }
- }
-
- [ConfigurationProperty("companyCode", DefaultValue = "", IsRequired = true)]
- public string CompanyCode
- {
- get { return (string)this["companyCode"]; }
- set { this["companyCode"] = value; }
- }
- }
- #endregion
Now, our Company Element is ready. We need to create a collection of type Company Element. To achieve this, we need to create a class CompanyElementCollection, which inherits from ConfigurationElementCollection, which is an abstract class, so we need to provide the implementation of its two abstract methods and will add ConfigurationCollection attribute to it. This ConfigurationCollection attribute has four parameters. First is the type of items, which this collection will contain and the remaining three parameters are AddItemName, ClearItemsName and RemoveItemName. If we don’t supply the AddItemName, we will get add tag instead of company tag.
- #region CompanyElement Collection
- [ConfigurationCollection(typeof(CompanyElement), AddItemName = "company")]
- public class CompanyElementCollection : ConfigurationElementCollection
- {
- protected override ConfigurationElement CreateNewElement()
- {
- return new CompanyElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element)
- {
- return ((CompanyElement)element).Name;
- }
-
- }
- #endregion
Now, we are done with creating an element and its collection. We will create a new class CompanySection, which inherits from ConfigurationSection. Our CompanySection will contain a public property of type company collection and decorated by an attribute ConfigurationProperty. We will add the name for the property collection and set IsDefaultCollection to true as it is our default collection.
- #region Company Section
- public class CompanySection : ConfigurationSection
- {
- [ConfigurationProperty("companies", IsDefaultCollection = true)]
- public CompanyElementCollection Companies
- {
- get { return (CompanyElementCollection)this["companies"]; }
- set { this["companies"] = value; }
- }
- }
- #endregion
We can have multiple sections in our Application, which can be grouped into one. To highlight this, I have created one more section.
- #region Settings Section
- public class SettingSection : ConfigurationSection
- {
-
- [ConfigurationProperty("countrycode", DefaultValue = "", IsKey = true, IsRequired = true)]
- public string CountryCode
- {
- get { return (string)this["countrycode"]; }
- set { this["countrycode"] = value; }
- }
- [ConfigurationProperty("isenabled", DefaultValue = true, IsRequired = true)]
- public bool IsEnabled
- {
- get { return (bool)this["isenabled"]; }
- set { this["isenabled"] = value; }
- }
-
- }
- #endregion
Now, we will create a new class MySectionGroup that inherits from ConfigurationSectionGroup. In this class, we will add two public properties that will return the sections, which we have created.
- #region MySection Group
- public class MySectionGroup : ConfigurationSectionGroup
- {
- [ConfigurationProperty("setting", IsRequired = false)]
- public SettingSection GeneralSettings
- {
- get { return (SettingSection)base.Sections["setting"]; }
- }
-
- [ConfigurationProperty("companySection", IsRequired = false)]
- public CompanySection ContextSettings
- {
- get { return (CompanySection)base.Sections["companySection"]; }
- }
- }
- #endregion
Now, we will build our class library project and create its DLL. Once our project builds successfully, we can test it by creating a console Application and add reference of our DLL to it. Open the App.config file and add the configSections, sectionGroup and section to it. We need to specify the name and fully qualified type of all the section and section group.
- <configSections>
- <sectionGroup name="mysection" type="CustomTags.MySectionGroup,CustomTags">
- <section name="companySection" type="CustomTags.CompanySection,CustomTags"/>
- <section name="settingSection" type="CustomTags.SettingSection,CustomTags"/>
- </sectionGroup>
- </configSections>
Note
configSections must be the first tag inside the configuration element, else you may end up in getting “The parameter 'sectionGroupName' is invalid” when you try to access a section from the code.
Now, we will add our custom tags in App.config file.
- <mysection>
- <settingSection countrycode="US" isenabled='true' />
- <companySection>
- <companies>
- <company name="Microsoft Corporation" shortName="MSFT" companyCode="MSFT"/>
- <company name="Yahoo" shortName="YHOO" companyCode="YHOO"/>
- </companies>
- </companySection>
- </mysection>
We will now try to access these tags from the code. The code snippet is given below for the same.
- class Program
- {
- static void Main(string[] args)
- {
- MySectionGroup group= ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).SectionGroups["mysection"] as MySectionGroup;
- foreach (ConfigurationSection section in group.Sections)
- {
- Console.WriteLine("\n\n===========================================");
- Console.WriteLine(section.SectionInformation.Name.ToUpper());
- Console.WriteLine("===========================================");
- if (section.GetType() == typeof(CompanySection))
- {
- CompanySection c = (CompanySection)section;
- CompanyElementCollection coll = c.Companies;
- foreach (CompanyElement item in coll)
- {
- Console.WriteLine("|{0,25}| {1,6}| {2,6}|", item.Name, item.ShortName , item.CompanyCode);
- Console.WriteLine("-------------------------------------------");
- }
- }
- else if (section.GetType() == typeof(SettingSection))
- {
- SettingSection s = (SettingSection)section;
- Console.WriteLine("|{0,2}| {1,5}| ", s.CountryCode, s.IsEnabled);
- Console.WriteLine("-------------------------------------------");
- }
-
-
- }
- Console.ReadLine();
- }
- }
Output
We have successfully created a custom section and we are able to access its values from the code but still we have one issue i.e. Visual Studio doesn’t provide intellisense for the tags. This is because Visual Studio does not contain the schema definition for our tags. To do this, we have a simple hack.Open App.config. Go to XML menu item in the menu bar and click Create the schema. This will create xsd file. Copy your element from this file.
- <xs:element name="mysection">
- <xs:complexType>
- <xs:sequence>
- <xs:element name="settingSection">
- <xs:complexType>
- <xs:attribute name="countrycode" type="xs:string" use="required" />
- <xs:attribute name="isenabled" type="xs:boolean" use="required" />
- </xs:complexType>
- </xs:element>
- <xs:element name="companySection">
- <xs:complexType>
- <xs:sequence>
- <xs:element name="companies">
- <xs:complexType>
- <xs:sequence>
- <xs:element maxOccurs="unbounded" name="company">
- <xs:complexType>
- <xs:attribute name="name" type="xs:string" use="required" />
- <xs:attribute name="shortName" type="xs:string" use="required" />
- <xs:attribute name="companyCode" type="xs:string" use="required" />
- </xs:complexType>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
Now, add a new schema file to your project and add the code given above in it and save it. Now, again go to App.config properties, browse your schema file and add it.
This will enable intellisense for your custom tags.