in a .NET configuration file. After wasting almost the entire night fighting with this problem, I thought I would let everyone know that it is possible, and easier than you might think. (By the way, this was a clear case of working too long on the problem. After a good nights sleep, I solved this in under 5 minutes.)At this point you might wonder why this was so difficult. The largest and most significant problem is the fact that NameValueCollection isn't serializable (KB 814187
). The reason for this is that NameValueCollection doesn't implement ICollection
but extends NameObjectCollectionBase
instead. The recommended solution is to use a SoapFormatter
to serialize and deserialize the collection. While using the SoapFormatter does work, it seemed like a very complex solution for a seemingly simple problem.Not wanting to over-engineer my solution, I started searching the web for alternatives. I found several newsgroup postings that all said to use the SoapFormatter since that was the recommended solution and the only way to go. Not having drunk too much blue kool-aid, I kept searching. That search turned up an article by Keyvan Nayyeri
that shows how to created a serializable NameValueCollection. This approach was intriguing and certainly seemed to be a more useful solution than using the SoapFormatter, but again it seemed like a lot of work.Keep in mind, all I wanted to be able to do in the configuration file was to create a section that looked like this:
1: <copyFiles>
2: <add name="C:\Windows" value="*.dll"/>
3: <add name="C:\Temp"/>
4: </copyFiles>
(".NET How To Create and Use Custom Name-Value Config Sections") that shows how to do this in 4 simple steps. Reading through the article, I realized that it is written using the the .NET 1.0 and 1.1 ConfigurationSettings
, which is provided for backwards compatibility only. Since the application I'm working on is .NET 3.5 and taking advantage of many of the new language features, I wanted to ensure that I wasn't using any deprecated classes.This is where I started running into additional problems. It seems that Microsoft provides a NameValueSectionHandler
, which the article makes us of. The problem is that NameValueSectionHandler is architected following the .NET 1.0/1.1 model and implements the System.Configuration.IConfigurationSectionHandler
interface. This is great if you want to use the deprecated ConfigurationSettings class; if you want to use the recommended System.Configuration.ConfigurationManager
or System.Web.Configuration.WebConfigurationManager
classes you are out of luck. They will only work with a class that derives from ConfigurationSection
.So, after all this I thought I was out of luck and would need to write my own configuration section handler. (Remember, this was about 1:00 AM.) After sleeping on it, I realized that there was a much simpler way. Along with the NameValueSectionHandler, Microsoft also implemented NameValueConfigurationCollection
and NameValueConfigurationElement
, which derive from the appropriate configuration classes to be used by the ConfigurationManager. After seeing that, I realized that all I needed to do was implement a NameValueSection which derives from ConfigurationSection.This is where the solution becomes easy. In it's simplest most form, the NameValueSection looks like this:
1: public class NameValueSection : ConfigurationSection
2: {3: [ConfigurationProperty("", IsDefaultCollection = true)]
4: public NameValueConfigurationCollection Settings
5: { 6: get 7: {8: return (NameValueConfigurationCollection)base[""];
9: } 10: } 11: }1: <sectionGroup name="customSettings">
2: <section name="copyFiles" type="NameValueSection, CustomConfiguration"/>
3: </sectionGroup>
4: <copyFiles>
5: <add name="C:\Windows" value="*.dll"/>
6: <add name="C:\Temp"/>
7: </copyFiles>
1: NameValueSection nameValueSection = ConfigurationManager.GetSection("copyFiles") as NameValueSection;
2: if (nameValueSection != null)
3: { 4: NameValueConfigurationCollection settings = nameValueSection.Settings;5: foreach (string key in settings.AllKeys)
6: {7: Console.WriteLine(settings[key].Name + ": " + settings[key].Value);
8: } 9: }posted on Sunday, March 16, 2008 8:00 PM

Feedback
1) encapsulate the copyFiles section into a customSettings section like:
<customSettings>
<copyFiles>
<add name="C:\Windows" value="*.dll"/>
<add name="C:\Temp"/>
</copyFiles>
</customSettings>
2) Also add this parent section into the GetSection call like in:
= ConfigurationManager.GetSection("customSettings/copyFiles")
"An error occurred creating the configuration section handler for customSettings/copyFiles: Could not load file or assembly 'CustomConfiguration' or one of its dependencies. The system cannot find the file specified." Any idea?
Thanks,
<configSections>
<sectionGroup name="customSettings">
<section name="copyFiles" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</sectionGroup>
</configSections>
<customSettings>
<copyFiles>
<add key="C:\Windows" value="*.dll"/>
<add key="C:\Temp"/>
</copyFiles>
</customSettings>
NameValueCollection settings = ConfigurationManager.GetSection("customSettings/copyFiles") as NameValueCollection;
if (settings != null)
{
foreach (string key in settings.AllKeys)
{
Response.Write(key + ": " + settings[key] + "<br />");
}
}
I was using above logic and encountered an error "Cannot load the config file".
Great solution! I am converting an app that uses extensive registry entries to use a custom config file. I couldn't access a NameValue Collection from the custom configuration until I found your solution.
Thank you!