Recently, I was needing to store information about 5 instances of an application server that I was accessing from my program. As I was entering the third set of values in <appSettings> section I decided to go a better route. What I wanted seemed simple: a section that worked exactly like the <connectionStrings> section, but with my own attributes. After several hours with the documentation and some on-line research, I got what I wanted. I was hoping it would only take 20-30 minutes. In order to provide you with the experience I hoped to have, I have written this post.
First, let show you what I wanted my section to look like:
<acmeConfiguration>
<add name="ERCOT"
connectionName="AcmeER" fileDir="\\server\path" baseUrl="http://server:9999" />
<add name="PM"
connectionName="AcmePM" fileDir="\\server2\path" baseUrl="http://serverxx:9901" />
</acmeConfiguration>
You will need to subclass three classes from System.Configuration namespace: ConfigurationSection, ConfigurationElementCollection, and ConfigurationElement which correspond to the <acmeConfiguration>, the set of elements within <acmeConfiguration>, and the <add> elements respectively. Understanding the above is the key to simplifying this exercise. Here’s the code that implements the section above:
public class AcmeConfigSection : ConfigurationSection {
[ConfigurationProperty("", IsRequired=true, IsDefaultCollection=true)]
public AcmeInstanceCollection Instances {
get { return (AcmeInstanceCollection) this[""]; }
set { this[""] = value; }
}
}
public class AcmeInstanceCollection : ConfigurationElementCollection {
protected override ConfigurationElement CreateNewElement() {
return new AcmeInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element) {
return ((AcmeInstanceElement) element).Name;
}
} public class AcmeInstanceElement : ConfigurationElement {
[ConfigurationProperty("name", IsKey=true, IsRequired=true)]
public string Name {
get { return (string) base["name"]; }
set { base["name"] = value; }
}
[ConfigurationProperty("connectionName", IsRequired=true)]
public string ConnectionName {
get { return (string)base["connectionName"]; }
set { base["connectionName"] = value; }
}
[ConfigurationProperty("fileDir", IsRequired = true)]
public string FileDir {
get { return (string)base["fileDir"]; }
set { base["fileDir"] = value; }
}
[ConfigurationProperty("baseUrl", IsRequired = true)]
public string BaseUrl {
get { return (string)base["baseUrl"]; }
set { base["baseUrl"] = value; }
}
}
Finally, for convenience, I created a class to provide static access to this collection similar to System.Configuration.ConfigurationManager.ConnectionStrings:
public class AcmeConfig {
protected static Dictionary<string, AcmeInstanceElement> _instances; static AcmeConfig() {
_instances = new Dictionary<string,AcmeInstanceElement>();
AcmeConfigSection sec = (AcmeConfigSection) System.Configuration.ConfigurationManager.GetSection("AcmeConfiguration");
foreach ( AcmeInstanceElement i in sec.Instances ) {
_instances.Add(i.Name, i);
}
} public static AcmeInstanceElement Instances(string instanceName) {
return _instances[instanceName];
}
private AcmeConfig() {
}
}
That’s it. I wish MS would put a sample like mine in the docs because I think this is what 99% of developers are looking for.