Custom configuration section with intelisense
Setup intelisense for custom configuration sections in app.config
Config files are really useful and allow you to store some setting values without having to hard-code them. It provides out of the box configuration utility which is very flexible, but because of its flexibility, it has some drawbacks as well.
For you application you can easily create your own, nonstandard elements and easily bind them to your strongly typed model in order to read values. However, after some time, if missed to be included in documentation it is really hard to know what values are valid, which ae mandatory etc. without having to take a look at the code implementation for that specific configuration section.
How to create your custom section will not be described in details in this article as it is really well explained in msdn documentation https://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.140).aspx
When you create your custom section and element and attributes structure in classes, it is left to create the structure in XML. Creating config xml in this stage is easy as you just finished your coding and you can quickly see what is where and fill the configuration. If you would have to change some value or add any attribute in future which is supported by class model it would be really hard to do without checking the code as there is no intellisense for your configuration xml.
I was searching on Google to find the way to do it, but most of the articles are mentioning VS configuration XSD which is related to your machine. It is not a good thing to do as it will only work on your machine if you change it. If someone else has to check, inellisense would not work for that person on that persons machine.
The way I figure out how to do it is to attach XSD to your config and VS will automatically pick it up. It will do it on everyone machine where config is opened for editing in VS.
So to start we will need some class structure for our config section element
using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Configuration.Sample{public sealed class CustomConfigSection : ConfigurationSection{public enum RGB{Red,Green,Blue}[ConfigurationProperty("xmlns", IsRequired = false)] public String Xmlns { get { return this["xmlns"] != null ? this["xmlns"].ToString() : string.Empty; } } [ConfigurationProperty("enumAttribute", DefaultValue = RGB.Red, IsRequired = false)] public RGB EnumAttribute { get { return (RGB)this["enumAttribute"]; } } [ConfigurationProperty("integerAttribute", DefaultValue = 0, IsRequired = false)] public int IntegerAttribute { get { return (int)this["integerAttribute"]; } } [ConfigurationProperty("stringAttribute", IsRequired = false)] public string StringAttribute { get { return (string)this["stringAttribute"]; } } [ConfigurationProperty("booleanAttribute", DefaultValue = true, IsRequired = false)] public bool BooleanAttribute { get { return (bool)this["booleanAttribute"]; } } [ConfigurationProperty("customConfigElement", IsRequired = false)] public CustomConfigElement CustomElement { get { return (CustomConfigElement)this["customConfigElement"]; } } } }
Important thing to do is to declare xmlns attribute on order to have XSD attached to config xml file later. Now when we have section element we will hae to create configuration element which will be part of the section
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Configuration.Sample { public sealed class CustomConfigElement : ConfigurationElement { [ConfigurationProperty("enumAttribute", DefaultValue = CustomConfigSection.RGB.Red, IsRequired = false)] public CustomConfigSection.RGB EnumAttribute { get { return (CustomConfigSection.RGB)this["enumAttribute"]; } } [ConfigurationProperty("integerAttribute", DefaultValue = 0, IsRequired = false)] public int IntegerAttribute { get { return (int)this["integerAttribute"]; } } [ConfigurationProperty("stringAttribute", DefaultValue = string.Empty, IsRequired = false)] public string StringAttribute { get { return (string)this["stringAttribute"]; } } [ConfigurationProperty("booleanAttribute", DefaultValue = true, IsRequired = false)] public bool BooleanAttribute { get { return (bool)this["booleanAttribute"]; } } } }
Now that we have our class structure, we can write our configuration in app.config file
<?xml version="1.0" encoding="utf-8" ?> <configuration> <!-- Custom config start--> <configSections> <section name="customConfigSection" type="Configuration.Sample.CustomConfigSection, Configuration.Sample" allowDefinition="Everywhere" allowLocation="true"/> </configSections> <customConfigSection xmlns="urn:Configuration.Sample" enumAttribute="Red" integerAttribute="12" stringAttribute="section text" booleanAttribute="true"> <customConfigElement enumAttribute="Red" integerAttribute="31" stringAttribute="element text" booleanAttribute="false"/> </customConfigSection> <!-- Custom config end--> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
To test whether we did all fine, we can test reading config value with a simple console app which insludes these two previously defined classes
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Configuration.Sample { class Program { static void Main(string[] args) { var configuration = ConfigurationManager.GetSection("customConfigSection") as CustomConfigSection; if (configuration != null) { Console.WriteLine("Configuration loaded"); } else { Console.WriteLine("Configuration loading failed"); } Console.ReadLine(); } } }
Now that we see that this is working we need to make this config file editable with intelisense. We'll do it with a custom schema XSD file.
To speed up things a little bit I used online service http://xmlgrid.net/xml2xsd.html to generate initial XSD from config section and modify it meet my project structure and types.
<?xml version="1.0" encoding="UTF-8"?> <xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:tns="urn:Configuration.Sample" targetNamespace="urn:Configuration.Sample" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="customConfigSection"> <xs:complexType> <xs:sequence maxOccurs="1"> <xs:element name="customConfigElement"> <xs:complexType> <xs:attribute name="enumAttribute" default="Red"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Red"/> <xs:enumeration value="Green"/> <xs:enumeration value="Blue"/> <xs:enumeration value="Red,Green"/> <xs:enumeration value="Red,Blue"/> <xs:enumeration value="Blue,Green"/> <xs:enumeration value="Red,Green,Blue"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="integerAttribute" type="xs:int" default="0"></xs:attribute> <xs:attribute name="stringAttribute" type="xs:string" default=""></xs:attribute> <xs:attribute name="booleanAttribute" type="xs:string" default="false"></xs:attribute> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="enumAttribute" default="Red"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Red"/> <xs:enumeration value="Green"/> <xs:enumeration value="Blue"/> <xs:enumeration value="Red,Green"/> <xs:enumeration value="Red,Blue"/> <xs:enumeration value="Blue,Green"/> <xs:enumeration value="Red,Green,Blue"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="integerAttribute" type="xs:int" default="0"></xs:attribute> <xs:attribute name="stringAttribute" type="xs:string" default=""></xs:attribute> <xs:attribute name="booleanAttribute" type="xs:string" default="false"></xs:attribute> </xs:complexType> </xs:element> </xs:schema>
In order to enable user to select more than one enumeration value you need to use "," in config xml which is equivalent to "|" in C#
After the change, try to edit for example enumAttribute value from VS. You will be amazed that intelisense is even there to make config file more easier for editing
References
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article