I’ve been using Phil Haack‘s Custom Configuration Sections in 3 Easy Steps for some time now, and my configuration file management has never been happier… until I needed to read a collection of configuration elements.
Here’s a sample of the XML I needed to incorporate:
<importer> <filespecs> <add type="EmployeeCSV" path="d:\import\employees.csv" /> <add type="OrderCSV" path="d:\import\orders_*.csv" /> </filespecs> </importer>
The typical solution is to write your own collection class (FilespecConfigurationElementCollection
), inheriting from ConfigurationElementCollection
, and ensuring your Filespec
object inherits from ConfigurationElement
. I figured there has to be a better way — and there is.
I wrote a generic version of ConfigurationElementCollection
, which you can use to avoid writing the custom collection class. The code that follows is the generic class, the Filespec
object I used, and the property declaration from my ConfigurationSettings
class (as described in Phil’s previously mentioned article).
Note one key part of this implementation: You must override the ToString()
method of your custom ConfigurationElement
class to return a unique value. The generic ConfigurationElementCollection
uses the ToString()
method to obtain a unique key for each element in the collection.
// The ConfigurationElementCollection<t> provides a simple generic implementation of ConfigurationElementCollection. [ConfigurationCollection(typeof(ConfigurationElement))] public class ConfigurationElementCollection</t><t> : ConfigurationElementCollection where T : ConfigurationElement, new() { protected override ConfigurationElement CreateNewElement() { return new T(); } protected override object GetElementKey(ConfigurationElement element) { return ((T)(element)).ToString(); } public T this[int idx] { get { return (T)BaseGet(idx); } } } // The Filespec class is an example of a custom configuration element. // Note that we inherit from ConfigurationElement, and use ConfigurationProperty attributes. public class Filespec : ConfigurationElement { public Filespec() { } [ConfigurationProperty("path", DefaultValue="", IsKey=true, IsRequired=true)] public string Path { get { return (string)(base["path"]); } set { base["path"] = value; } } [ConfigurationProperty("type", IsKey=false, IsRequired = true)] public FilespecType Type { get { return (FilespecType)(base["type"]); } set { base["type"] = value; } } public override string ToString() { return this.Path; } } // Finally, our ConfigurationSettings class (only part of the class is included). // Note how we use the generic ConfigurationElementCollection. public class ConfigurationSettings : ConfigurationSection { // ... [ConfigurationProperty("filespecs", IsRequired=true)] public ConfigurationElementCollection<filespec> FileSpecs { get { return (ConfigurationElementCollection</filespec><filespec>)this["filespecs"]; } } // ... }
If you’re using a number of ConfigurationElementCollection
s, this is a great way to simplify your code.
Ron says:
Great, but how to use this on an existing config file ?
Ron says:
Ok, I got it to work. Great.