Hack 48. Extend the Application Configuration File
Use an open source custom tool to add your own
configuration sections to your app.config
file.
Custom configuration sections are a valuable
tool for storing application-specific settings. With .NET, you can
extend the normal configuration files and include your own custom
configuration sections. Normally, creating your own custom
configuration section involves writing classes and a configuration
section handler to access your custom settings. There is an easier
and much quicker way to handle the creation of a custom configuration
section and all the code that you need to work with it.
The open source custom tool, ConfigBuilder, can be used to automatically
generate configuration section handlers, as well as custom objects
that can be used as data containers for the settings.
To use the
ConfigBuilder custom tool, you will need
to download and run the installation file from http://workspaces.gotdotnet.com/configbuilder.
6.6.1. Building the Configuration Template
To start using
ConfigBuilder, you
first need to add a new
XML file to your project. This XML file
is called the configuration template file and
will be used to specify the format of your configuration section.
This file will then used to generate the code to access this
configuration section. The configuration template can be named
whatever you like and should be in the location where you want the
code to be stored. (The code will appear underneath this XML file in
the Solution Explorer, exactly like other custom tool-generated
code.)
The first step to building your configuration template file is to add
a root element named
configuration, the same as a normal
configuration file. Next you will need to declare your custom
sections by creating an element for the section and then any number
of attributes. The name of the section and the name of the attributes
should be the same as the names that will be used in the
configuration section, but instead of specifying values, you will
need to specify the type that this attribute expects. To specify the
type, you will need to use
C# type keywords such as
string, int,
short, and so forth.
Here is an example of declaring a custom section called
DatabaseSettings:
<configuration>
<DatabaseSettings server="string"
username="string" timeout="int" />
</configuration>
In this file, I am creating a single element called
DatabaseSettings that has three different
attributes; the first two are strings and the last is an integer.
You can also define default values for
attributes in the template file.
In this example, suppose that I want to set a default value for the
timeout attribute parameter. I can do this by
simply adding a colon after the type and then specifying my default
value. My template file would now look like this:
<configuration>
<DatabaseSettings server="string"
username="string" timeout="int: 60" />
</configuration>
By declaring a default value, the attribute is no longer required. If
the timeout attribute is omitted in the
configuration section for this application config
file, the code will simply assume the value is 60. (When declaring
default values for strings, you must use [Empty]
to signify an empty string.)
You can also specify the number of times a section can appear, create
section groups, and specify comments to be included in the generated
code. Explanations of these features can all be found in
ConfigBuilder's documentation.
6.6.2. Running the ConfigBuilder
To execute the template file and
generate the code, you need to specify
ConfigBuilder
as the custom tool for this file. This is done by simply entering
ConfigBuilder into the Custom Tool field in
the .XML file's property window
as shown in Figure 6-7.
Once ConfigBuilder is specified as the custom tool for this file, it
will be run every time the file is saved, regenerating the code based
on any changes to the template file.
After you specify ConfigBuilder as the custom tool, a plus sign will
appear next to the .XML file. The plus sign can be expanded to show
the code that has been generated based on the template. Here is a
look at the code generated based on the template from the preceding
example (I have removed comments for the sake of
brevity):
namespace ConfigBuilderExample
{
using System;
using System.Configuration;
using System.Xml;
public sealed class DatabaseSettingsSectionHandler
: object, IConfigurationSectionHandler
{
public const string SectionName = "DatabaseSettings";
public static DatabaseSettingsConfig Config
{
get
{
object oConfig = ConfigurationSettings.GetConfig(
DatabaseSettingsSectionHandler.SectionName);
if ((oConfig = = null))
{
throw new ConfigurationException(
"The application configuration file must have the
\'DatabaseSettings\' configuration" +
" section defined.");
}
return ((DatabaseSettingsConfig)(oConfig));
}
}
object IConfigurationSectionHandler.Create(object parentConfig,
object webContext, XmlNode sectionNode)
{
XmlElement sectionElement = ((XmlElement)(sectionNode));
return DatabaseSettingsConfig.Create(sectionElement);
}
internal static object GetValue(XmlElement element,
string attributeName,
Type attributeType,
string defaultVal)
{
try
{
string attrVal = element.GetAttribute(attributeName);
if ((attrVal.Length = = 0))
{
if ((defaultVal = = null))
{
throw new ConfigurationException(String.Format(
"Missing value for required attribute \'{0}\' of
element \'{1}\'.", attributeName, element.Name));
}
attrVal = defaultVal;
}
if (attributeType.IsEnum)
{
return Enum.Parse(attributeType, attrVal);
}
return Convert.ChangeType(attrVal, attributeType,
System.Threading.Thread.CurrentThread.CurrentCulture);
}
catch (ConfigurationException )
{
throw;
}
catch (Exception ex)
{
throw new ConfigurationException(String.Format(
"Failed to extract value for attribute \'{0}\'
from element \'{1}\'.", attributeName,
element.Name), ex);
}
}
}
public class DatabaseSettingsConfig
{
private String _server;
private String _username;
private int _timeout;
public DatabaseSettingsConfig(String server,
String username,
int timeout)
{
this._server = server;
this._username = username;
this._timeout = timeout;
}
public String Server
{
get{return this._server;}
}
public String Username
{
get{return this._username;}
}
public int Timeout
{
get{return this._timeout;}
}
internal static DatabaseSettingsConfig Create(
XmlElement configElement)
{
String server = ((String)(
DatabaseSettingsSectionHandler.GetValue(
configElement, "server", typeof(String), null)));
String username = ((String)(
DatabaseSettingsSectionHandler.GetValue(
configElement, "username",
typeof(String), null)));
int timeout = ((int )(
DatabaseSettingsSectionHandler.GetValue(
configElement, "timeout", typeof(int ), " 60")));
return new DatabaseSettingsConfig(
server, username, timeout);
}
}
}
6.6.3. Using the Code
To use this
configuration
section in your configuration file, you need to declare your custom
section in the configSections section of the
configuration file for your application:
<configSections>
<section name="DatabaseSettings"
type="ConfigBuilderExample.DatabaseSettingsSectionHandler" />
</configSections>
ConfigBuilderExample is the name of my namespace
in this example. Next, you need to actually add your configuration
section to the application configuration file:
<configuration>
<DatabaseSettings server="localhost"
username="notsa" timeout="55" />
</configuration>
Then you can read these values from the configuration file using the
following code anywhere in your application:
DatabaseSettingsConfig config = (DatabaseSettingsConfig)
System.Configuration.ConfigurationSettings.GetConfig(
"DatabaseSettings");
MessageBox.Show(config.Server);
The first line of this code gets an instance of the
DatabaseSettingsConfig class from the
configuration file. You can then access any of the configuration
settings by simply accessing the properties of this class. In this
example, the value of the server element is shown to the user through
a message box.
This custom tool is a great time-saver and makes it extremely easy to
create and use custom configuration sections.
|
No comments:
Post a Comment