I have my app.config file with an ArrayOfString entry. Each entry contains a semi-colon delimited string. I want to be able to use lambda, if possible, to parse out the values from a List<> based on input criteria. But I want the first entry it finds bases on that criteria. Or is there a better way USING the app.config file?
For instance ..
If I wanted to find the first entry that contained the [source],[filetype] and just return the file path.
Example app.config entry.
SOURCE;FLAC;112;2;\\sourcepath\music\
DEST;FLAC;112;2;\\destpath\music\
Rather than relying on having your values fall at the correct index of a string split operation, you should create your own ConfigurationSection definition.
See the How To on MSDN and the MSDN ConfigurationProperty example.
Here is some code to get you started:
class CustomConfig : ConfigurationSection
{
private readonly CustomElementCollection entries =
new CustomElementCollection();
[ConfigurationProperty("customEntries", IsDefaultCollection = true)]
[ConfigurationCollection(typeof(CustomElementCollection), AddItemName = "add")]
public CustomElementCollection CustomEntries { get { return entries; } }
}
class CustomElementCollection : ConfigurationElementCollection
{
public CustomElement this[int index]
{
get { return (CustomElement) BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new CustomElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CustomElement)element).Name;
}
}
class CustomElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return this["name"] as string; }
set { this["name"] = value; }
}
[ConfigurationProperty("direction", IsRequired = true)]
public string Direction
{
get { return this["direction"] as string; }
set { this["direction"] = value; }
}
[ConfigurationProperty("filePath", IsRequired = true)]
public string FilePath
{
get { return this["filePath"] as string; }
set { this["filePath"] = value; }
}
}
Once you have your custom configuration specified, then you can Select with a lambda using any property specified in your custom ConfigurationElement.
Related
I have found a great example for custom configuration handler and tried to use it for my own implementation.
I have set up App.config like this:
<configSections>
<section name="DocumentationSettings" type="ConfigHandler.DocumentationSettings,Settings"/>
</configSections>
<DocumentationSettings>
<DocumentationSections>
<DocumentationSection Id="AAA">
<SectionDescription Value="SectionDescriptionAAA"/>
</DocumentationSection>
<DocumentationSection Id="BBB">
<SectionDescription Value="SectionDescriptionBBB"/>
</DocumentationSection>
<DocumentationSection Id="CCC">
<SectionDescription Value="SectionDescriptionCCC"/>
</DocumentationSection>
</DocumentationSections>
</DocumentationSettings>
I use this code to access my custom configuration:
DocumentationSettings documentationSettings = ConfigurationManager.GetSection("DocumentationSettings") as DocumentationSettings;
foreach (DocumentationSectionConfigElement section in (documentationSettings.DocumentationSections.Sections))
{
Console.WriteLine(section.Id);
Console.WriteLine(section.SectionDescription.Properties.Value);
}
First 'Console.WriteLine' works perfect.
So I have the following problems and implementation related questions:
I get error on second 'Console.WriteLine', error: Unrecognized attribute 'Value'. I have put "public SectionDescription SectionDescription" since "DocumentationSectionConfigElement" Class exposes property access, but I might be wrong, I tried first to put it into "DocumentationSectionCollection" but I don't know how to implement it there and for me it seems that "DocumentationSectionCollection" only implements "Collection" logic.
I want to access "fields" either both like this:
section.Id
section.SectionDescription.Value
or like this:
section.Properties.Id
section.SectionDescription.Properties.Value
I see that "Collection" allows to use these properties directly using indexer methods like this:
public DocumentationSectionConfigElement this[int index]
But I can't implement indexer method on "SectionDescription" class, because it is a single section not a collection, so this "Properties" name persists when I access fields.
What do I need to add to be able to use LINQ on these configuration objects?
I mean like this:
(documentationSettings.DocumentationSections.Sections).Select(x => x.Id)
Are there any really good examples of complex XML structure configuration handler? From those which I found there were mostly simple structures like these:
but no any example of complex structure like this:
<section>
<subSections>
<subSection name="111">
<Description Value="AAA"></Description>
<Headers>
<Header type="Main">
<Properties>
<Property name="Property1"/>
<Property name="Property2"/>
<Property name="Property3"/>
</Properties>
</Header>
</Headers>
</subSection>
</subSections>
</section>
which is more close to real scenarios when you need to better organize application configuration.
What is the point of "sectionGroup" tag in "configSections" if "Custom Configuration Handler" works and it is enough with one "section" registered ?
Why all this stuff so complex? I have spent so much time these "custom configuration" things that shouldn't be so complex I believe. Aren't there any Visual Studio Add-ins that handle this stuff and generate code based on XML configuration structure?
Here is my Configuration Handler implementation:
public class DocumentationSettings : ConfigurationSection
{
[ConfigurationProperty("DocumentationSections")]
public DocumentationSections DocumentationSections
{
get { return (DocumentationSections)base["DocumentationSections"]; }
}
}
public class DocumentationSections : ConfigurationElement
{
[ConfigurationProperty("", IsDefaultCollection = true)]
public DocumentationSectionCollection Sections
{
get { return (DocumentationSectionCollection)base[""]; }
}
}
public class SectionDescription : ConfigurationElement
{
[ConfigurationProperty("SectionDescription")]
public new SectionDescriptionConfigElement Properties
{
get { return (SectionDescriptionConfigElement)base["SectionDescription"]; }
}
}
public class DocumentationSectionCollection : ConfigurationElementCollection
{
public DocumentationSectionCollection()
{
DocumentationSectionConfigElement details = (DocumentationSectionConfigElement)CreateNewElement();
if (details.Id != "")
{
Add(details);
}
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
protected override ConfigurationElement CreateNewElement()
{
return new DocumentationSectionConfigElement();
}
protected override Object GetElementKey(ConfigurationElement element)
{
return ((DocumentationSectionConfigElement)element).Id;
}
public DocumentationSectionConfigElement this[int index]
{
get { return (DocumentationSectionConfigElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
new public DocumentationSectionConfigElement this[string name]
{
get { return (DocumentationSectionConfigElement)BaseGet(name); }
}
public int IndexOf(DocumentationSectionConfigElement details)
{
return BaseIndexOf(details);
}
public void Add(DocumentationSectionConfigElement details)
{
BaseAdd(details);
}
protected override void BaseAdd(ConfigurationElement element)
{
BaseAdd(element, false);
}
public void Remove(DocumentationSectionConfigElement details)
{
if (BaseIndexOf(details) >= 0)
BaseRemove(details.Id);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Remove(string name)
{
BaseRemove(name);
}
public void Clear()
{
BaseClear();
}
protected override string ElementName
{
get { return "DocumentationSection"; }
}
}
public class DocumentationSectionConfigElement : ConfigurationElement
{
[ConfigurationProperty("Id", IsRequired = true, IsKey = true)]
[StringValidator(InvalidCharacters = " ~!##$%^&*()[]{}/;’\"|\\")]
public string Id
{
get { return (string)this["Id"]; }
set { this["Id"] = value; }
}
[ConfigurationProperty("Name", IsRequired = false)]
public string Name
{
get { return (string)this["Name"]; }
set { this["Name"] = value; }
}
[ConfigurationProperty("SectionDescription")]
public SectionDescription SectionDescription
{
get { return ((SectionDescription)base["SectionDescription"]); }
}
}
public class SectionDescriptionConfigElement : ConfigurationElement
{
[ConfigurationProperty("Value", IsRequired = true)]
public string Value
{
get { return (string)this["Value"]; }
set { this["Value"] = value; }
}
}
Since no activity here, I will try answering my questions one by one:
Found this nice article that describes a lot of things and shows somewhat complex example of custom configuration implementation.
I found this great tool - ".NET Configuration Code Generator", that does exactly what I wanted - it takes XML structure and generates custom configuration code.
Concider the following config section:
<PluginSection>
<Plugins>
<Plugin name="Plug1">
<add MessageType="1" MessageSubType="1" Ringtone="chime.wav" Vibrate="1000:0:1"/>
<add MessageType="1" MessageSubType="2" Ringtone="chime2.wav" Vibrate="1000:0:1"/>
</Plugin>
<Plugin name="Plug2">
<add MessageType="1" MessageSubType="1" Ringtone="chime.wav"/>
<add MessageType="1" MessageSubType="2" Ringtone="chime2.wav"/>
<add MessageType="2" Ringtone="chime3.wav"/>
</Plugin>
</Plugins>
</PluginSection>
I've implemented the parsing of this as a c# IConfigSectionHandler. Now I understand that this method is deprecated, and that I should use ConfigurationSection, ConfigurationElements and ConfigurationElementCollections. I have no problem understanding the examples of this on the web (msdn and SO). But all the examples I've seen so far have used one of the properties as a key. My elements are unique as a combination of the Plugin name, MessageType and MessageSubType. The MessageSubType is also optional. Could i parse a config section that looks like this using the recommended classes, or do I have to alter my configuration to fit the regime of the ConfigurationClasses by e.g. adding a "dummy" key?
No.
But to avoid keys you need to do more work.
The concrete type KeyValueConfigurationCollection allows easy creation of a configuration collection by setting some properties.
To create a more customised collection requires either extending the abstract ConfigurationElementCollection (but this will still be based on the add/remove/clear model as used by <appSettings>. but allowing the element names to be configured but this is still based on ahaving a key value for each member of the collection (this is determined by an override of GetElementKey so does not need to be directly included in the XML).
Alternatively you can create you own, completely custom configuration collection by extending ConfigurationElement, but you'll need to do all the work of parsing the child elements yourself (remember ConfigurationElementCollection is itself a child class of ConfigurationElement).
So based on the excelent answer from Richard, i decided to rewrite my config parsing. Heres the result:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace PluginConfiguration
{
public class PluginConfigSection : ConfigurationSection
{
[ConfigurationProperty("Plugins", IsDefaultCollection = true)]
[ConfigurationCollection(typeof(PluginCollection), AddItemName = "Plugin")]
public PluginCollection Plugins
{
get
{
PluginCollection coll = (PluginCollection)base["Plugins"];
return coll;
}
}
}
public class PluginCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new MessageMappingElementCollection();
}
protected override object GetElementKey(ConfigurationElement element)
{
MessageMappingElementCollection coll = element as MessageMappingElementCollection;
return coll.Name;
}
}
public class MessageMappingElementCollection : ConfigurationElementCollection
{
[ConfigurationProperty("name", IsRequired = true, IsKey = true)]
public string Name
{
get { return this["name"].ToString(); }
set { this["name"] = value; }
}
protected override ConfigurationElement CreateNewElement()
{
return new MessageMappingElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
MessageMappingElement msgElement = element as MessageMappingElement;
string ret = String.Format("{0}|{1}", msgElement.MessageType, msgElement.MessageSubType);
return ret;
}
}
public sealed class MessageMappingElement : ConfigurationElement
{
public MessageMappingElement()
{
MessageType = 0;
MessageSubType = 0;
RingTone = "";
Description = "";
Vibrate = "";
}
[ConfigurationProperty("MessageType", IsRequired = true)]
public int MessageType
{
get { return int.Parse(this["MessageType"].ToString()); }
set { this["MessageType"] = value; }
}
[ConfigurationProperty("MessageSubType", IsRequired = false)]
public int MessageSubType
{
get { return int.Parse(this["MessageSubType"].ToString()); }
set { this["MessageSubType"] = value; }
}
[ConfigurationProperty("RingTone", IsRequired = false)]
public string RingTone
{
get { return this["RingTone"].ToString(); }
set { this["RingTone"] = value; }
}
[ConfigurationProperty("Description", IsRequired = false)]
public string Description
{
get { return this["Description"].ToString(); }
set { this["Description"] = value; }
}
[ConfigurationProperty("Vibrate", IsRequired = false)]
public string Vibrate
{
get { return this["Vibrate"].ToString(); }
set { this["Vibrate"] = value; }
}
}
}
I'm in the process of attempting to remove some old legacy references and I'm now working on something I've never had to try doing before. Let's say I have a config file section like this:
<customSection>
<customValues>
<custom key="foo" invert="True">
<value>100</value>
</custom>
<custom key="bar" invert="False">
<value>200</value>
</custom>
</customValues>
</customSection>
I've now created ConfigurationSection, ConfigurationElement, and ConfigurationElementCollection classes to correctly read all of this out. Here they are for reference (it's basically all boiler plate except for the ValueElement class which overrides the Deserialize method to get at the element's value):
public class CustomSection : ConfigurationSection
{
[ConfigurationProperty("customValues")]
[ConfigurationCollection(typeof(CustomValueCollection), AddItemName = "custom")]
public CustomValueCollection CustomValues
{
get { return (CustomValueCollection)this["customValues"]; }
}
}
public class CustomValueCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new CustomElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CustomElement) element).Key;
}
public CustomElement this[int index]
{
get { return (CustomElement) BaseGet(index); }
}
new public CustomElement this[string key]
{
get { return (CustomElement) BaseGet(key); }
}
public bool ContainsKey(string key)
{
var keys = new List<object>(BaseGetAllKeys());
return keys.Contains(key);
}
}
public class CustomElement : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get { return (string)this["key"]; }
}
[ConfigurationProperty("invert", IsRequired = true)]
public bool Invert
{
get { return (bool)this["invert"]; }
}
[ConfigurationProperty("value", IsRequired = true)]
public ValueElement Value
{
get { return (ValueElement)this["value"]; }
}
}
public class ValueElement : ConfigurationElement
{
private int value;
//used to get value of element, not of an attribute
protected override void DeserializeElement(System.Xml.XmlReader reader, bool serializeCollectionKey)
{
value = (int)reader.ReadElementContentAs(typeof(int), null);
}
public int Value
{
get { return value; }
}
}
What I'm now stuck on is this business requirement: if a CustomElement has an Invert value of true, then invert the value of the Value property in the associated ValueElement. So if I accessed the value of "value" under "foo", I would get -100.
Does anyone have any idea how to either pass something like that in to the ValueElement object or make the ValueElement aware of its parent CustomElement to be able to get at that Invert property? My initial thought is to do the check in the Value property getter of the CustomElement class, and if Invert is true, then modify the ValueElement object there, but I'm open to other ideas.
The goal here is to remove the legacy code without touching the config file, otherwise I'd push the "value" subelement up into the parent as an attribute.
Thanks
By the look of it, you just need to modify the Value property getter to include your invert logic. I can see no reason why that wouldn't work.
You could add another property that gets the raw value as well.
[ConfigurationProperty("value", IsRequired = true)]
public int Value
{
get
{
var result = (ValueElement)this["value"];
return Invert ? result.Value * -1 : result.Value;
}
}
I am writing a custom configuration section using the .NET configuration api. I want to define a section that can have exactly one of two sub-elements,
e.g.
<MyCustomSection>
<myItems>
<myItemType name="1">
<firstSubTypeConfig />
</myItemType>
<myItemType name="2">
<secondSubTypeConfig />
</myItemType>
</myItems>
</MyCustomSection>
Currently, I have the sections defined like:
public class MyCustomSection : ConfigurationSection
{
public override bool IsReadOnly()
{
return false;
}
[ConfigurationProperty("myItems")]
public MyItemsCollection MyItems
{
get { return (MyItemsCollection)base["myItems"]; }
set { base["myItems"] = value; }
}
}
[ConfigurationCollection(typeof(MyItemType), AddItemName="myItemType",
CollectionType=ConfigurationElementCollectionType.BasicMap)]
public class MyItemsCollection : ConfigurationElementCollection
{
public override bool IsReadOnly()
{
return false;
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
protected override string ElementName
{
get { return "myItemType"; }
}
protected override ConfigurationElement CreateNewElement()
{
return new MyItemType();
}
protected override object GetElementKey(ConfigurationElement element)
{
return (element as MyItemType).Name;
}
}
public class MyItemType : ConfigurationElement
{
public override bool IsReadOnly()
{
return false;
}
[ConfigurationProperty("name", IsRequired=true)]
public string Name
{
get { return (string)base["name"]; }
set { base["name"] = value; }
}
[ConfigurationProperty("firstSubTypeConfig")]
public FirstSubTypeConfig FirstSubTypeConfig
{
get { return (FirstSubTypeConfig)base["firstSubTypeConfig"]; }
set { base["firstSubTypeConfig"] = value; }
}
[ConfigurationProperty("secondSubTypeConfig")]
public SecondSubTypeConfig SecondSubTypeConfig
{
get { return (SecondSubTypeConfig)base["secondSubTypeConfig"]; }
set { base["secondSubTypeConfig"] = value; }
}
}
public class FirstSubTypeConfig : ConfigurationElement
{
public override bool IsReadOnly()
{
return false;
}
}
public class SecondSubTypeConfig : ConfigurationElement
{
public override bool IsReadOnly()
{
return false;
}
}
The configuration is also modified and saved programmatically, and currently, saving the
configuration section will add the secondSubTypeConfig element even if I only specify the
firstSubTypeConfig element.
My first thought was to introduce a common base class for FirstSubTypeConfig and SecondSubTypeConfig, but I'm not clear on if or how the configuration api would handle that.
How do I setup mutually exclusive custom config elements?
I'm not sure this is the "correct" approach, but this works.
The code as written in the question will load the config file fine. You can check the ElementInformation.IsPresent property to validate that exactly one element is included.
e.g.
var custom = (MyCustomSection)ConfigurationManager.GetSection("MyCustomSection");
foreach (MyItemType itemType in custom.MyItems)
{
if (itemType.FirstSubTypeConfig.ElementInformation.IsPresent
&& itemType.SecondSubTypeConfig.ElementInformation.IsPresent)
{
throw new ConfigurationErrorsException("At most one of firstSubTypeConfig or secondSubTypeConfig can be specified in a myItemType element");
}
else if (!itemType.FirstSubTypeConfig.ElementInformation.IsPresent
&& !itemType.SecondSubTypeConfig.ElementInformation.Ispresent)
{
throw new ConfigurationErrorsException("Either a firstSubTypeConfig or a secondSubTypeConfig element must be specified in a myItemType element");
}
}
As for saving the config, it seems that checking for ElementInformation.IsPresent and explicitly setting it to null will prevent the element from being written to the config file. e.g.
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var custom = (MyCustomSection)config.GetSection("MyCustomSection");
//make modifications against the custom variable ...
foreach (MyItemType itemType in custom.MyItems)
{
if (!itemType.FirstSubTypeConfig.ElementInformation.IsPresent)
itemType.FirstSubTypeConfig = null;
if (!itemType.SecondSubTypeConfig.ElementInformation.IsPresent)
itemType.SecondSubTypeConfig = null;
}
config.Save();
I think that you should do that on the PostDeserialize method ihnerited from the ConfigurationElement class without doing that on your business logic.
For example:
protected override void PostDeserialize()
{
base.PostDeserialize();
if (FirstSubTypeConfig != null && SecondTypeCOnfig != null)
{
throw new ConfigurationErrorsException("Only an element is allowed.");
}
}
for configuration as following
<MyCollection default="one">
<entry name="one" ... other attrubutes />
... other entries
</MyCollection>
when implement a MyCollection, what should i do for the "default" attribute?
Let's suppose you have this .config file:
<configuration>
<configSections>
<section name="mySection" type="ConsoleApplication1.MySection, ConsoleApplication1" /> // update type & assembly names accordingly
</configSections>
<mySection>
<MyCollection default="one">
<entry name="one" />
<entry name="two" />
</MyCollection>
</mySection>
</configuration>
Then, with this code:
public class MySection : ConfigurationSection
{
[ConfigurationProperty("MyCollection", Options = ConfigurationPropertyOptions.IsRequired)]
public MyCollection MyCollection
{
get
{
return (MyCollection)this["MyCollection"];
}
}
}
[ConfigurationCollection(typeof(EntryElement), AddItemName = "entry", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class MyCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new EntryElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
if (element == null)
throw new ArgumentNullException("element");
return ((EntryElement)element).Name;
}
[ConfigurationProperty("default", IsRequired = false)]
public string Default
{
get
{
return (string)base["default"];
}
}
}
public class EntryElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true, IsKey = true)]
public string Name
{
get
{
return (string)base["name"];
}
}
}
you can read the configuration with the 'default' attribute, like this:
MySection section = (MySection)ConfigurationManager.GetSection("mySection");
Console.WriteLine(section.MyCollection.Default);
This will output "one"
I don't know if it's possible to have a default value in a ConfigurationElementCollection. (it doesn't seen to have any property for default value).
I guess you have to implement this by yourself. Look at the example below.
public class Repository : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get { return (string)this["key"]; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return (string)this["value"]; }
}
}
public class RepositoryCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new Repository();
}
protected override object GetElementKey(ConfigurationElement element)
{
return (element as Repository).Key;
}
public Repository this[int index]
{
get { return base.BaseGet(index) as Repository; }
}
public new Repository this[string key]
{
get { return base.BaseGet(key) as Repository; }
}
}
public class MyConfig : ConfigurationSection
{
[ConfigurationProperty("currentRepository", IsRequired = true)]
private string InternalCurrentRepository
{
get { return (string)this["currentRepository"]; }
}
[ConfigurationProperty("repositories", IsRequired = true)]
private RepositoryCollection InternalRepositories
{
get { return this["repositories"] as RepositoryCollection; }
}
}
Here's the XML config:
<myConfig currentRepository="SQL2008">
<repositories>
<add key="SQL2008" value="abc"/>
<add key="Oracle" value="xyz"/>
</repositories>
</myConfig>
And then, at your code, you access the default item using the following:
MyConfig conf = (MyConfig)ConfigurationManager.GetSection("myConfig");
string myValue = conf.Repositories[conf.CurrentRepository].Value;
Of course, the MyConfig class can hide the details of accessing the Repositories and CurrentRepository properties. You can have a property called DefaultRepository (of type Repository) in MyConfig class to return this.
This may be a bit late but may be helpful to others.
It is possible but with some modification.
ConfigurationElementCollection inherits ConfigurationElement as such "this[string]" is available in ConfigurationElement.
Usually when ConfigurationElementCollection is inherited and implemented in another class, the "this[string]" is hidden with "new this[string]".
One way to get around it is to create another implementation of this[] such as "this[string, string]"
See example below.
public class CustomCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new CustomElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CustomElement)element).Name;
}
public CustomElement this[int index]
{
get { return (CustomElement)base.BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
// ConfigurationElement this[string] now becomes hidden in child class
public new CustomElement this[string name]
{
get { return (CustomElement)BaseGet(name); }
}
// ConfigurationElement this[string] is now exposed
// however, a value must be entered in second argument for property to be access
// otherwise "this[string]" will be called and a CustomElement returned instead
public object this[string name, string str = null]
{
get { return base[name]; }
set { base[name] = value; }
}
}
If you want to genericize it, this should help:
using System.Configuration;
namespace Abcd
{
// Generic implementation of ConfigurationElementCollection.
[ConfigurationCollection(typeof(ConfigurationElement))]
public class ConfigurationElementCollection<T> : ConfigurationElementCollection
where T : ConfigurationElement, IConfigurationElement, new()
{
protected override ConfigurationElement CreateNewElement()
{
return new T();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((IConfigurationElement)element).GetElementKey();
}
public T this[int index]
{
get { return (T)BaseGet(index); }
}
public T GetElement(object key)
{
return (T)BaseGet(key);
}
}
}
Here's the interface referenced above:
namespace Abcd
{
public interface IConfigurationElement
{
object GetElementKey();
}
}