|
|
Advanced Configuration Section Manipulation | | Few things in .Net are as powerful, or as obscure as app.config and web.config. Many people think these files are little more than glorified .ini files wrapped in XML. I've heard this more times than I can mention and I can understand why some may think that. XML seems to be one of those things that, no matter how lame your implementation is, if you are using it then you're 'cool.' As I mentioned in Going Remote, you can use .config files within the Remoting framework so you can actually add new types to your remoting framework without having to recompile the application. In an enterprise scenario, this is huge. To be honest, I think you could write a rather lengthy book on .config files in .NET and to be honest, I can't think of where the 'endpoint' would actually be since they are so flexible and powerful. In this article, I'm going to walk you through creating a custom configuration section. Now, what I'm doing here isn't that revolutionary becuase you can essentially get to the same place using AppSettings. However appSettings is a predefined section in AppConfig and what if you had 100+ settings. Chances are you'd want to group them logically into some sane order, the same way you use Namespaces and regions within your code.
Anyway, here's some code to use for a custom configuration section handler:
First, I create a class that models the configuration values I want to use. There's nothing special about this class and I intentionally used a silly example to highlight the fact that you can do whatever you want with it.
public class BillRyan
{
#region Private Variables
private System.String mWhatIsBillListeningTo;
private System.String mGirlfriend;
private System.String mBillsSideKick;
private System.String mRollingPaperBalls;
private System.String mStudying1;
private System.String mStudying2;
#endregion
#region Public Methods
public System.String WhatIsBillListeningTo
{
get{return mWhatIsBillListeningTo;}
set{mWhatIsBillListeningTo = value;}
}
public System.String BillsSideKick
{
get{return mBillsSideKick;}
set{mBillsSideKick = value;}
}
public System.String Girlfriend
{
get{return mGirlfriend;}
set{mGirlfriend = value;}
}
public System.String RollingPaperBalls
{
get{return mRollingPaperBalls;}
set{mRollingPaperBalls = value;}
}
public System.String Studying1
{
get{return mStudying1;}
set{mStudying1 = value;}
}
public System.String Studying2
{
get{return mStudying2;}
set{mStudying2 = value;}
}
#endregion
public BillRyan(){}
}
|
Now that I have a class in place, I create a Handler class to manage it. The key thing to remember is that the class must implement the System.Configuration.IConfigurationSectionHandler interface in order for it to work. The class definition and implementation is provided below:
using System;
using System.Xml;
using System.Configuration;
namespace KDN.Configuration
{
/// <summary>
/// Summary description for BillRyanConfigSectionHandler.
///
public class BillRyanConfigSectionHandler : System.Configuration.IConfigurationSectionHandler
{
public BillRyanConfigSectionHandler(){}
#region IConfigurationSectionHandler Members
public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
try
{
BillRyan Settings = new BillRyan();
if (section == null)
{
return Settings;
}
XmlNode CurrentAttribute;
XmlAttributeCollection NodeAttributes = section.Attributes;
XmlAttributeCollection FileAttributes = null;
CurrentAttribute = NodeAttributes.RemoveNamedItem("file");
if (CurrentAttribute != null)
{
string Directory = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory,
CurrentAttribute.Value);
if (System.IO.File.Exists(Directory))
{
XmlDocument FileDocument = new XmlDocument();
FileDocument.Load(Directory);
XmlNode SettingsNode = FileDocument.SelectSingleNode("ProgramInfo");
if (SettingsNode != null)
{
FileAttributes = SettingsNode.Attributes;
}
}
}
Settings.BillsSideKick = GetAttributeValue("BillsSideKick", NodeAttributes, FileAttributes);
Settings.WhatIsBillListeningTo = GetAttributeValue("WhatIsBillListeningTo", NodeAttributes, FileAttributes);
Settings.Girlfriend = GetAttributeValue("Girlfriend", NodeAttributes, FileAttributes);
Settings.RollingPaperBalls = GetAttributeValue("RollingPaperBalls", NodeAttributes, FileAttributes);
Settings.Studying1 = GetAttributeValue("Studying1", NodeAttributes, FileAttributes);
Settings.Studying2 = GetAttributeValue("Studying2", NodeAttributes, FileAttributes);
return Settings;
}
catch (Exception exc)
{
throw new ConfigurationException("Error loading iDec configuration.", exc, section);
}
}
private string GetAttributeValue( string attributeName, XmlAttributeCollection nodeAttributes,
XmlAttributeCollection fileAttributes)
{
XmlNode CurrentAttribute = null;
string ReturnValue = null;
if (fileAttributes != null)
{
CurrentAttribute = fileAttributes.RemoveNamedItem(attributeName);
}
if (CurrentAttribute != null)
{
ReturnValue = CurrentAttribute.Value.Trim();
}
else
{
CurrentAttribute = nodeAttributes.RemoveNamedItem(attributeName);
if (CurrentAttribute != null)
{
ReturnValue = CurrentAttribute.Value.Trim();
}
}
return ReturnValue;
}
#endregion
}
}
|
Finally, here's the actual configuration file:
<configuration>
<configSections>
<section name="ProgramInfo"
type="KDN.Configuration.BillRyanConfigSectionHandler, WebConfigRocks" />
configSections>
<ProgramInfo type="KDN.Configuration.BillRyanConfigSectionHandler, WebConfigRocks"
WhatIsBillListeningTo="Back Down - 50 Cent"
Girlfriend="Kim"
BillsSideKick="Sonny the Coding Cuckoo"
RollingPaperBalls="true"
Studying1="Sharepoint"
Studying2="Sql Server 2005"/>
configuration> |
First let me point out that the one downside to using .config files is that they are very fussy and they aren't forgiving when it comes to typos. In this particular solution, I have two assemblies ConfigOMania and WebConfigRocks. The WebConfigRocks is probably a bad choice of a name because this particular application is a Winforms app but naming stuff has never been my strong point. This is just a standard app.config file that when compiled will produce the name applicationName.exe.config - this is done automatically, you don't have to mess with it. Also notice that the classes above are both seated in the KDN.Configuration whereas the main Winform and the config file reside in the KDN.ConfigOMania namespace. So first, I create a configsection - I can have as many of these as I want and one way to group them is to have them correspond to the assemblies in your solution(s). I give the section the name "ProgramInfo". Now, I create a node outside of configSections called ProgramInfo. The type attribute needs to first specify the fully qualified class name with a comma immediately following it (a space after it will cause you a problem - yes, it's fussy) followed by the Assembly name. Each attribute corresponds to a property in my BillRyan class. This much couldn't be any easier. The only tricky part (which isn't very tricky at all) is implementing the Create method of the System.Configuration.IConfigurationSectionHandler interface. All I'm actually doing though is creating an instance of my BillRyan object and setting the properties inside of it. When I have this in place, I simply call the static .GetConfig method of the System.Configuration.ConfigurationSettings class passing in the name of the section I want to use (ProgramInfo) and viola', it'll retrieve my settings and allow me to work with them in a very clean manner without having to do any XML parsing. To use it, this is all that needs to be done:
private void button1_Click(object sender, System.EventArgs e)
{
BillRyan BillsSettings = (BillRyan)System.Configuration.ConfigurationSettings.GetConfig("ProgramInfo");
lb.Items.Add(BillsSettings.BillsSideKick); //Back Down - 50 Cent
lb.Items.Add(BillsSettings.RollingPaperBalls); //true
lb.Items.Add(BillsSettings.Girlfriend); //Kim
lb.Items.Add(BillsSettings.Studying1); //Sharepoint
lb.Items.Add(BillsSettings.Studying2); //Sql Server 2005
}
|
|
|