Introduction
I've been using the System.Configuration
classes in all kinds of applications and services. I find them very useful, and until recently, it was all that I needed to work with the XML application settings.
Background
I've made a Visual Studio add-in. I intend to publish an article about that too. Actually, I got the idea on writing this article about configuration settings while I was working on the add-in. So, this add-in has a main form that you can click on in order to set some options and start the action. The first idea that came to my mind was to add the App.config file to store the predefined options and load them while starting. The next second, I realized that there’s no exe output from my solution so I cannot have an App.config file. Moreover, it would be nice if the options, when modified, could be persisted into the config file. Well, that’s the intrigue.
Explaining the code
There are two classes:
ConfigurationSettingsRW
,
AppSettingsRW
and an interface:
IConfigurationRWSectionHandler
corresponding to the ones in the System.Configuration
namespace.
'RW' stands for 'Read Write'.
IConfigurationRWSectionHandler interface
public interface IConfigurationRWSectionHandler
{
object Create(XmlNode section);
void Persist(object config, XmlNode section);
}
The Create
method implementation should take care of loading the data from the XmlNode
into an instance of the desired type (e.g. a collection of some kind) that would be returned.
The Persist
method implementation should consider the first parameter of the same type as the instance returned by the Create
method and should modify the XmlNode
parameter accordingly.
ConfigurationSettingsRW class
You have the AppSettings
property and the GetConfig
method of course, but they're not static. The config file path has to be passed as a string to the constructor when creating an instance of this class => no more naming conventions for the config file. There are two public methods dealing with the appSettings
config section: LoadAppSettings
and SaveAppSettings
, with no parameters and no return (void
). You have nothing in the AppSettings
property until you call LoadAppSettings
. Setting or adding a value of a specified key in appSettings
is made by using the indexer of AppSettings
with the string parameter and calling SaveAppSettings
afterwards.
Besides the GetConfig
public method, you have the corresponding SetConfig
public method. Both are calling a private method named GetSectionData
for getting the section XmlNode
and the section handler interface. I did nothing about section groups... because I never used them. Feel free to extend the code in order to suit your needs.
private void GetSectionData(string sectionName,
out XmlNode sectionNode, out IConfigurationRWSectionHandler handler)
{
sectionNode = null;
handler = null;
try
{
XmlDocument configXml = new XmlDocument();
configXml.Load(this.configFilePath);
XmlNode node =
configXml.SelectSingleNode(string.Format("configuration" +
"/configSections/section[@name='{0}']", sectionName));
if (node != null)
{
sectionNode =
configXml.SelectSingleNode(string.Format("configuration/{0}",
sectionName));
string typeName = node.Attributes["type"].Value;
Type type = Type.GetType(typeName);
handler = (IConfigurationRWSectionHandler)
Activator.CreateInstance(type);
}
}
catch (Exception ex)
{
throw new ConfigurationException(string.Format("Error" +
" while loading {0} section.", sectionName), ex);
}
}
The SetConfig
method saves the data by calling the specified (by 'configuration/configSections/section/@type' value) IConfigurationRWSectionHandler.Persist
method. Of course, GetConfig
gets the data by calling the corresponding IConfigurationRWSectionHandler.Create
method.
AppSettingsRW class
The constructor has to get the config file path as a string parameter.
There is the AppSettings
public property of type NameValueCollection
.
There are the LoadFromFile
and SaveToFile
public methods that do exactly what their names suggest. Check this out:
public void LoadFromFile()
{
if (appSettings == null)
{
try
{
XmlDocument configXml = new XmlDocument();
configXml.Load(this.configFilePath);
XmlNode appSettingsNode =
configXml.SelectSingleNode("configuration/appSettings");
if(appSettingsNode.LocalName=="appSettings")
{
NameValueSectionHandler handler =
new NameValueSectionHandler();
appSettings = new NameValueCollection(
(NameValueCollection)handler.Create
(null, null, appSettingsNode));
}
}
catch (Exception ex)
{
throw new ConfigurationException("Error while loading appSettings." +
" Message: " + ex.Message, ex);
}
}
}
public void SaveToFile()
{
if (appSettings != null)
{
try
{
XmlDocument configXml = new XmlDocument();
configXml.Load(this.configFilePath);
XmlNode appSettingsNode =
configXml.SelectSingleNode("configuration/appSettings");
appSettingsNode.RemoveAll();
for (int i = 0; i < appSettings.Count; i++)
{
string key = appSettings.GetKey(i);
string val = appSettings.Get(i);
XmlNode node = configXml.CreateNode
(XmlNodeType.Element, "add", "");
XmlAttribute attr = configXml.CreateAttribute("key");
attr.Value = key;
node.Attributes.Append(attr);
attr = configXml.CreateAttribute("value");
attr.Value = val;
node.Attributes.Append(attr);
appSettingsNode.AppendChild(node);
}
configXml.Save(this.configFilePath);
}
catch (Exception ex)
{
throw new ConfigurationException
("Error while saving appSettings.", ex);
}
}
}
And there are two other public methods: GetValue
and SetValue
. I'm sure you guessed what these are for. Check the source file for details.
Using the code
You can include the source file (ConfigurationSettingsRW.cs) in your project and use the classes in the same manner as you'd use the System.Configuration
classes, with the few differences and additions described above. Check out the available sample solution and sources (ConfigurationSettingsRW_src.zip) to see how it's done. The config file's schema must be the one you know from the .NET Framework, with the 'configSections
', 'appSettings
', and everything else.
As I mentioned, I intend to write an article about some add-in. That would be a good usage sample, and I guess I could insert a link to it as soon as I get it done. Until then, you have a demo project available for download.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.