Configuration Section Handlers
(by John Roberts for Blayd Software)
Note: this article relates to the .Net Framework version 2.0, some
of the classes and class members referenced in this article were not available in
earlier framework versions.
Introduction
Configuration settings for .Net Framework applications are stored, unsurprisingly,
in configuration files, the settings for a Windows Forms application are stored
in an app.config file and the settings for an ASP.Net web application are stored
in (one or more) web.config files. In the case of a Windows Forms application, the
"app" part of the configuration file name represents the name of the application's
assembly, therefore the configuration file for an assembly named NumberCruncher.exe
would be named NumberCruncher.exe.config. In ASP.Net web applications the configuration
file is literally named web.config for all web applications, regardless of the name
of the compiled web application's assembly.
Configuration settings are stored as XML, therefore, configuration files have a
hierarchical structure. The configuration data is stored in a section element, which
may or may not contain child section elements, a sectionGroup element may, optionally,
be used to organise configuration section elements. Before a sectionGroup or section
element can be used in a configuration file to store configuration data it has to
be declared within a configSections element, if a sectionGroup or section is not
declared it will not be recognised by the configuration system. A sectionGroup or
section element can be declared in the machine.config file's configSections element
if it is to be used by more than one application or it can be declared in the app.config
file of the relevant application.
The configuration settings data contained in section elements is loaded by the .Net
Framework configuration system using configuration section handler classes. A configuration
section is associated with a specific configuration section handler class in the
type attribute of the section's declaration in the configSections section. A configuration
section element is associated with a single configuration section handler class
but different instances of the same configuration section handler class may be associated
with many configuration section elements. The .Net Framework configuration system
uses the section element's name to access the data in the section's declaration,
in the configSections section, to determine and initialize the relevant configuration
section handler class and then instructs the handler to load the section's configuration
settings data.
The configuration section handler classes that ship with the .Net Framework are
contained in the System.Configuration namespace. Developers can use the configuration
section handler classes defined in this namespace to load specific kinds of configuration
section data from their configuration files or they can develop their own, custom,
configuration section handler classes by extending the base classes contained in
the System.Configuration namespace. Prior to version 2.0 of the .Net Framework,
a configuration section handler class had to implement the IConfigurationSectionHandler
interface. However the IConfigurationSectionHandler interface has now been deprecated,
although it still remains in the framework as it is used internally. For .Net Framework
version 2.0 and above the ConfigurationSection abstract base class must be extended
to provide a custom configuration section handler. Extending the ConfigurationSection
base class and using either the declarative (attribute) model or the programmatic
model to define configuration section attributes is much more straight forward than
implementing the IConfigurationSectionHandler interface used to be, because, amongst
other things, it does not require any XML parsing in the derived class.
In this article we will be concentrating mainly on developing custom configuration
section handlers, however, we will briefly look at some of the handlers that ship
with the .Net Framework and also describe the settings required when declaring a
configuration section handler.
.Net Framework Built-in Configuration Section Handlers
AppSettingsSection Configuration Section Handler
The configuration file section that is probably most familiar to .Net Framework
developers is the appSettings section. This configuration section is easily accessible
in code from the static ConfigurationManager.AppSettings property which returns
a NameValueCollection derived class instance containing the key and value of each
entry in the appSettings configuration section. The actual Type of the value returned
from the AppSettings property is KeyValueInternalCollection which is an internal
type that derives from the public NameValueCollection type. The AppSettings property
casts this internal type to NameValueCollection and therefore it is safe to retrieve
the value of the AppSettings property directly into a NameValueCollection field
or local variable.
An example of an appSettings configuration section:
<?xml encoding="utf-8" version="1.0" ?>
<configuration>
<appSettings>
<add key="host" value="smtp.domain.com" />
<add key="userId" value="appbox@abusiness.co.uk" />
<add key="password" value="........" />
<add key="timeOut" value="60000" />
</appSettings>
</configuration>
The System.Configuration.AppSettingsSection class is declared as the configuration
section handler for the appSettings configuration section in the machine.config
file. Because, by default, the appSettings configuration section declaration is
inherited by all app.config files on the machine you do not have to include a configSections
element or an appSettings section declaration in your app.config or web.config file.
If your configuration requirements are fairly straight forward, the appSettings
section may be all you need, however, as it does not have a way of grouping settings,
the appSettings section can become messy and confusing if you have a lot of configuration
settings with groups of settings referring to different areas of functionality within
your application.
NameValueSectionHandler Configuration Section Handler
The example appSettings section above stores the fictitious settings for logging
into an SMTP server to send email from the application and looks fine, however,
when we add the settings for just one of the email types that we want to send from
the application things start to get confusing.
An example of an appSettings configuration section that stores
multiple groups of data:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="host" value="smtp.domain.com" />
<add key="userId" value="appbox@abusiness.co.uk" />
<add key="password" value="........" />
<add key="timeOut" value="60000" />
<add key="mailFrom" value="appbox@abusiness.co.uk" />
<add key="mailTo" value="sysbox@abusiness.co.uk" />
<add key="subject" value="Contact Us" />
</appSettings>
</configuration>
The example appSettings section now stores the data required to login to the SMTP
server and the data required to send one specific type of email. We now have two
distinct groups of configuration data in a single configuration section. As the
application develops it is likely that we will want to add even more configuration
settings and therefore we will end up with a single configuration section that is,
potentially, confusing and difficult to maintain. A far better solution would be
to have a configuration section for each group of settings data. We could still
use the appSettings section for the login settings, for example and then use a different
section for the email types, however, in this example we are going to use a configuration
section for each group of settings.
An example of an app.config file with two, user defined,
configuration sections:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="smtpHostSettings"
type="System.Configuration.NameValueSectionHandler,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<section name="contactUsSettings"
type="System.Configuration.NameValueSectionHandler,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</configSections>
<smtpHostSettings>
<add key="host" value="smtp.domain.com" />
<add key="userId" value="appbox@abusiness.co.uk" />
<add key="password" value="........" />
<add key="timeOut" value="60000" />
</smtpHostSettings>
<contactUsSettings>
<add key="mailFrom" value="appbox@abusiness.co.uk" />
<add key="mailTo" value="sysbox@abusiness.co.uk" />
<add key="subject" value="Contact Us" />
</contactUsSettings>
</configuration>
The app.config file now contains a configSections element in which we have declared
two configuration sections, smtpHostSettings and contactUsSettings, for details
of the available section element's attributes see the Declaring a Custom
Configuration Section and Handler section below. The NameValueSectionHandler class is declared as
the configuration section handler for both of the sections and because this is an
application specific declaration we have to include the declaration in the app.config
file. If we do not make the declaration here the two, custom, configuration sections
will not be recognised by the configuration system and we will get an exception
when the configuration data is loaded. The NameValueSectionHandler class, because
it has been around for a while, implements the IConfigurationSectionHandler interface,
rather than extend the ConfigurationSection base class, which also illustrates that,
although depreciated, the interface is still present and can still be used, however,
we recommend that you do not use depreciated types in new development projects.
Retrieving the settings from a user defined configuration
section:
string host = string.Empty;
NameValueCollection settings = ConfigurationManager.GetSection("smtpHostSettings")
as NameValueCollection;
if (settings != null)
{
if (!string.IsNullOrEmpty(settings["host"]))
{
host = settings["host"];
}
// ...
}
Accessing the settings data in the custom sections is fairly straight forward although,
as with accessing any external data, you should code defensively when reading configuration
settings, you can't be certain that the values are present in the configuration
file and you can't be certain that they will contain what they should. The ConfigurationManager.GetSection
method takes the name or the path of the configuration section, in the case of the
example above the name of the section works fine, however, if the section was contained
within a group, for example, we would need to specify the section's path e.g. "groupName/sectionName".
The value returned from the GetSection method has to be cast to the relevant type,
in the case of the example above we are casting to NameValueCollection as this is
the base type returned when using the NameValueSectionHandler, the actual type returned
is a private ReadOnlyNameValueCollection instance, however, we need to use its public,
inherited NameValueCollection properties in order to access it. How do we know that
the NameValueSectionHandler results in a NameValueCollection derived class being
returned from the GetSection method? Good question, basically it is matter of trial
and error, i.e. write the code generically using an Object as the return Type and
then examine the return value in the debugger!. When using a configuration section
handler that derives from the ConfigurationSection base class you can cast to the
Type of the derived class, we will be discussing and demonstrating the use of ConfigurationSection
derived section handlers later in the article. A cast is always required and therefore,
if you don't want an exception, you should code defensively by using a conditional
casting operation i.e. the as operator in C# or the TryCast function in VB. When
we have the values from the configuration section it is then just a matter of retrieving
each setting. In the example, we are retrieving the settings from the collection,
specifying the key of each setting, when using a ConfigurationSection derived section
handler class the settings are represented by the properties of the section handler
class. Having seen how to use a configuration section handler that implements the
(depreciated) IConfigurationSectionHandler interface and hinted about the alternative
approach offered by the ConfigurationSection base class you are probably starting
to appreciate why an alternative approach was required. However, we have a bit
more ground to cover before we get to the topic of developing your own configuration
section handler using the ConfigurationSection base class.
ConnectionStringsSection Configuration Section Handler
As mentioned earlier in this section the ConfigurationManager provides a convenience
static property, AppSettings, that can be used to retrieve the appSettings configuration
section data. ConfigurationManager also provides a static ConnectionStrings property
that can be used to retrieve the connectionStrings configuration section data. The
connectionStrings section is associated with the ConnectionStringsSection configuration
section handler. The ConnectionStringsSection class is declared as the configuration
section handler for the connectionStrings configuration section in the machine.config
file. Because, by default, the connectionStrings configuration section declaration
is inherited by all app.config files on the machine you do not have to include a
configSections element or a connectionStrings section declaration in your app.config
or web.config file.
The ConfigurationManager.ConnectionStrings property returns a ConnectionStringSettingsCollection
instance which contains a ConnectionStringSettings instance for each connectionString
section defined in the connectionStrings section of the configuration file. Retrieving
the connection string settings is just a matter of accessing the relevant properties
of the relevant ConnectionStringSettings instance.
If you need to specify database connection strings in your configuration file you
may well have to include login details, including a password, in your connection
string. We know that the recommended strategy is to use integrated security when
accessing a database, however, out here in the real world that is not always possible.
For example, if you have a website, using anonymous access, that requires a connection
to a database, integrated security may not be practical or may not be supported
by your hosting package, therefore, you will have to include the database user ID and password
in the connection string. Even the simple email example used earlier
in this section requires a password to be stored in the configuration file. Sensitive
data such as passwords really should be protected when it is stored in a configuration
file. The .Net Framework configuration system includes functionality to protect
or encrypt sensitive configuration settings data and will automatically decrypt
the data as it is loaded and before it is passed to the relevant configuration section
handler. The encryption of configuration settings does not relate directly to the
topic of this article and won't be covered here as we don't really have the space
to do justice to the topic. If you require further information about encrypting
configuration settings, you should consult the .Net Framework documentation or read
the following Blayd Software article: Protected Configuration.
IgnoreSection Configuration Section Handler
The final built-in configuration section handler that we are going to discuss is
the IgnoreSection configuration handler. When the .Net Framework configuration system
processes a configuration file it completely parses the file and will throw an exception
if it finds a configuration section that has not be declared in the configSections
section of the current configuration file or one of the files above the current
file in the configuration file inheritance chain. The IgnoreSection configuration
section handler can be used to instruct the .Net Framework configuration system
to bypass i.e. not to load and parse the configuration section that is associated
with the IgnoreSection handler. To get the configuration system to ignore a configuration
section you still have to declare the section in the configSections element, however,
you associate the section with the IgnoreSection handler class to ensure that the
configuration system bypasses the section and does not throw an exception during
the parsing operation.
If you instruct the configuration system to ignore a section in the configuration
file then you will have to load and parse the section and assign the configuration
settings appropriately from your own code in order to make use of the configuration
settings. The .Net Framework configuration system is very flexible and extensible
in most areas, this article, for example, is discussing one example of the configuration
system's extensibility, creating your own configuration section handlers, you can
also create your own settings provider and protected configuration provider if your
requirements cannot be fulfilled by the built-in providers. Ultimately however,
there may be occasions when you want to or have to load and parse a configuration
section directly from your own code and the IgnoreSection configuration section
handler facilitates this by allowing you to instruct the .Net Framework configuration
system to bypass or ignore the relevant section without throwing an exception.
Declaring a Custom Configuration Section and Handler
In order to include a user defined configuration section with a standard or custom
configuration section handler in a configuration file you first have to declare
the section and associate it with a configuration section handler in the configSections
section element of the machine.config file, if the section is to be made available
to all applications on the machine or the relevant app.config or web.config file.
To declare a user defined configuration section, create a
configSections section element in the relevant configuration file, if one does not
already exist, then add a section element as follows:
<configSections>
<section />
</configSections>
A user defined configuration section is declared using the available attributes
of the section element. The available attributes, their use and possible values
are described below, you should note that all attribute values should be assigned
as quoted strings, therefore, for example, a Boolean attribute has two possible
values "true" or "false". In addition,
attribute names and their values are case sensitive.
- allowDefinition
-
The
allowDefinition attribute is optional and only applies to ASP.Net applications.
The attribute specifies which configuration file or files the section can be used
in, the possible values are as follows:
- Everywhere
-
Allows the section to be used in any configuration file. This is the default value
i.e. if the
allowDefinition attribute is not specified Everywhere is assumed.
- MachineToApplication
-
Allows the section to be used in the following configuration files:
machine.config,
the root web.config file or the web.config file for an application.
Note: Both the machine.config and the root web.config files are
located in the following folder:
%SystemRoot%\Microsoft.NET\Framework\versionNumber\Config
- MachineToWebRoot
- Allows the section to be used in the
machine.config or the root web.config file.
- MachineOnly
- Allows the section to be used only in the
machine.config file.
- allowExeDefinition
-
The
allowExeDefinition attribute is optional and only applies to Windows Forms applications.
The attribute specifies which configuration file or files the section can be used
in, the possible values are as follows:
- MachineToApplication
-
Allows the section to be used in either the
machine.config or the app.config file.
This is the default value i.e. if the allowExeDefinition attribute is not specified
MachineToApplication is assumed.
- MachineToRoamingUser
-
Allows the section to be used in the following configuration files:
machine.config,
app.config or the user.config file in the roaming user folder.
- MachineToLocalUser
-
Allows the section to be used in the following configuration files:
machine.config,
app.config or the user.config file in either the local or roaming user folder.
- MachineOnly
- Allows the section to be used only in the
machine.config file.
- allowLocation
-
The
allowLocation (Boolean) attribute is optional and only applies to ASP.Net applications.
The attribute specifies whether the section can be used within the location element.
The default value is "true".
- name
-
The
name attribute is mandatory and specifies the name of the configuration section
element i.e. the section element name as it appears in the body of the configuration
file.
- restartOnExternalChanges
-
The
restartOnExternalChanges (Boolean) attribute is optional and only applies to
Windows Forms applications. The attribute specifies whether the application should
restart if the data for the configuration section changes. The default value is
"true".
- type
-
The
type attribute is mandatory and specifies the assembly qualified name of the
configuration section handler class. The value takes the following form:
Fully qualified class name, assembly file name, version, culture, public key token
The configuration section handler class may be in the assembly that is loading the
configuration or it may be in an external assembly. If the configuration section
handler class is in an external assembly the .Net Framework runtime must be able
to locate the assembly. Place an external assembly in the same folder as the configuration
file (the bin folder for an ASP.Net application), the Global Assembly Cache or the
%SystemRoot%\Microsoft.NET\Framework\version folder for the machine.config or root
web.config files.
Custom Configuration Section Handlers
If none of the built-in .Net Framework configuration section handler classes meet
your requirements you can design your own configuration section structure and then
develop a section handler class to process the section's data at runtime. As described
earlier in this article, the .Net Framework's configuration system associates a
configuration section with an XML element in the configuration file, in addition,
a configuration section may have one or more child XML elements. The ConfigurationSection
abstract base class should be extended to provide a configuration section handler,
an instance of the handler class maps to the relevant configuration section XML
element. The name of the section handler class does not have to match the name of
the relevant XML element, as a section handler class relates to a particular type
of configuration section structure rather than a specific configuration section,
the association with a specific configuration section is made in the configSections
declaration. A configuration section element, represented by a class derived from
the ConfigurationSection class, may have one or more child XML elements, each represented
by a class that derives from the ConfigurationElement abstract base class. The ConfigurationSection
class itself derives from the ConfigurationElement class and can, therefore, be
thought of as specialized version of a configuration element i.e. a section element.
However, in reality, a section element can be represented by a single, simple, XML
tag or it can have a more complex structure containing other section and/or single
XML elements. The actual configuration settings data in a configuration section
is represented by one or more instances of the ConfigurationProperty class stored
in the ConfigurationElement instance's ConfigurationPropertyCollection (known as
the property bag). Individual configuration properties can be accessed, from a derived
class, using the protected ConfigurationElement indexer (Item) property or the entire
collection can be accessed using the protected ConfigurationElement.Properties property.
A ConfigurationProperty instance may represent an attribute of a configuration section
or a simple XML element or it may represent a child XML element or it may represent
a collection of child XML elements. To allow calling code to get and optionally
set the configuration property values, the ConfigurationSection derived class should
have properties that wrap the underlying ConfigurationProperty instances. For example,
to implement a configuration property named "attOne" the ConfigurationSection derived
custom section handler class would create, either declaratively or programmatically,
a ConfigurationProperty instance named "attOne" and would also implement a public
property named "AttributeOne" to get and optionally set the value of the underlying
ConfigurationProperty instance. We will use the simple configuration section below
to try to make sense of all this and to clarify which class is used to represent
which configuration entity.
Configuration section example:
<customSection attOne="valueOne" attTwo="valueTwo">
<customElement attThree="valueThree" attFour="valueFour" />
</customSection>
In the example above, the customSection XML element would be represented by a section
handler class derived from the ConfigurationSection class. The handler class would
have three properties, each represented by an underlying instance of the ConfigurationProperty
class. Two of the properties would represent the attributes of the customSection
XML element, attOne and attTwo, one property would represent the customElement child
XML element. The three ConfigurationProperty instances, representing the configuration
section properties would be contained in the (inherited) ConfigurationPropertyCollection
of the ConfigurationSection derived class. The customElement child XML element would
be represented by a class derived from the ConfigurationElement class and would
have two properties, each represented by an underlying instance of the ConfigurationProperty
class. The properties would represent the two attributes of the customElement XML
element, attThree and attFour. The two ConfigurationProperty instances, representing
the attributes of the customElement XML element would be contained in the (inherited)
ConfigurationPropertyCollection of the ConfigurationElement derived class.
A configuration section that contains more than one child XML element can be handled
by extending the ConfigurationElementCollection abstract base class. By default,
classes that extend the ConfigurationElementCollection base class represent a collection
of ConfigurationElement instances implemented with add, remove and clear directives
that are used to modify any inherited properties and specify new ones.
The .Net Framework provides two options for developing a custom configuration section
handler, the declarative or attribute model and the programmatic model. Both models
require the development of a ConfigurationSection derived handler class. The declarative
model requires you to decorate your handler's properties with the relevant attributes
to instruct the .Net Framework configuration system to create the actual ConfigurationProperty
instances. The programmatic model requires you to create the actual ConfigurationProperty
instances in the handler's code. The declarative model is probably the simplest
way to create a custom configuration section handler, however, the programmatic
model offers more control and flexibility. We will examine and demonstrate both
models in the next two sections of this article.
Whichever model you choose to implement you should take advantage of the classes
contained in the System.Configuration namespace to validate your configuration property
settings. Validation of configuration properties can be implemented either with
attributes or by directly assigning a validation class instance when initializing
an instance of the ConfigurationProperty class. The use of validation attributes
is limited to the declarative model, validation attributes do not work on property
wrappers implemented using the programmatic model.
Configuration property validation classes:
IntegerValidator and IntegerValidatorAttribute
-
Provides validation of an
Int32 configuration property value. Checks an integer
against a specified minimum and maximum value. The range can be specified as either
inclusive i.e. the specified value must be between the specified minimum and maximum
or exclusive i.e. the specified value must not be between the specified minimum
and maximum.
LongValidator and LongValidatorAttribute
-
Provides validation of an
Int64 configuration property value. Checks a long integer
against a specified minimum and maximum value. The range can be specified as either
inclusive i.e. the specified value must be between the specified minimum and maximum
or exclusive i.e. the specified value must not be between the specified minimum
and maximum. When using LongValidator you can also specify a resolution value that
must be matched for the validation to pass.
RegexStringValidator and RegexStringValidatorAttribute
-
Provides validation of a
String configuration property value based on the rules
provided by a regular expression.
StringValidator and StringValidatorAttribute
-
Provides validation of a
String configuration property value. Checks either the
minimum length or the minimum and maximum length of the specified value. You can
also specify a string of characters that are not allowed in the specified value
i.e. invalid characters.
TimeSpanValidator and TimeSpanValidatorAttribute
-
Provides validation of a
TimeSpan configuration property value. Checks a time span
against a specified minimum and maximum value. The range can be specified as either
inclusive i.e. the specified value must be between the specified minimum and maximum
or exclusive i.e. the specified value must not be between the specified minimum
and maximum. When using TimeSpanValidator you can also specify a resolution in seconds
value that must be matched for the validation to pass.
As mentioned earlier, a ConfigurationSection derived custom configuration section
handler class should implement a property to wrap each of its ConfigurationProperty
instances. The validation classes detailed above can be used to validate the property
values before they are set. The validation strategy that you use i.e. validation
attribute or validation class depends on how you create the ConfigurationProperty
instances that are to represent the configuration settings. If you are using the
declarative model in your ConfigurationSection derived class you should decorate
each of the property wrappers with a validation attribute. If you are using the
programmatic model in your ConfigurationSection derived class you should assign
an instance of the relevant validation class to each of the ConfigurationProperty
instances as they are created. When implementing a validation strategy there is
one important point to note, the base classes load the actual settings from the
configuration file and assign the values to the relevant ConfigurationProperty instances,
the property setters in the ConfigurationSection derived section handler class will
not be called during this process. Therefore, if you want to validate configuration
property values as they are loaded from the configuration file, you must either
decorate the properties, in the ConfigurationSection derived class, that are representing
ConfigurationProperty instances, with a validation attribute or assign an instance
of the relevant validation class to each ConfigurationProperty instance as it is
created. Implementing validation in a property setter will not work as the setter
will not be called during the load process. The relevant property getter will be
called when a configuration property is read from code and the property setter will
be called when a configuration property is updated from code but during the initial
load, parse and set process the validation class specified in the ConfigurationProperty
instance, either directly or via an attribute, is used to verify configuration settings.
If your validation requirements cannot be met using the existing validation classes
in the System.Configuration namespace and they will almost certainly not be in a
surprising number of cases, you will have to develop your own validation and/or
validation attribute class or classes. A custom configuration property validation
class is created by extending the ConfigurationValidatorBase abstract base class
and implementing your own Validate method to check the contents of the relevant
configuration property. When using the programmatic model you pass an instance of
the ConfigurationValidatorBase derived class to the constructor of the relevant
ConfigurationProperty instances when you create them in code. The configuration
system will call the Validate method inherited by the derived class before it assigns
the configuration property's value. When using the declarative model, the configuration
system uses the values specified in the validation attribute to initialize the relevant
validation class and then calls its Validate method to perform the validation check.
Therefore, a configuration property must be decorated with a validation attribute
in order for the validation class to be utilized. If the custom validation class
does not require any runtime parameters in order to perform the validation you can
use the ConfigurationValidatorAttribute class directly to decorate the relevant
configuration property passing the Type of the custom validation class to the attribute's
constructor e.g. [ConfigurationValidator(typeof(CustomValidator))] where CustomValidator
is the Type of the ConfigurationValidatorBase derived class. However, in order to
facilitate reuse, the custom validation class will require parameters specified
at runtime to perform the validation check e.g. the maximum number of characters
allowed in a configuration property. To provide runtime parameter values to the
validation class you will need to develop your own validation attribute class by
extending the ConfigurationValidatorAttribute base class and then use the derived
instance's properties to initialize the relevant validation class in the ValidatorInstance
property e.g. the derived attribute class could have a MaximumLength property, the
value of which is passed to the constructor of the validation class in the ValidatorInstance
property.
Declarative Model Configuration Section Handler
When using the declarative or attribute model to create a configuration section
handler we need to extend the ConfigurationSection abstract base class to implement
a handler for the configuration section XML element. We then need to create a property
getter and optionally a setter for each of the configuration settings in the configuration
section XML element. A configuration setting may be an XML attribute of the section
XML element or it may be an XML child element of the section XML element or it may
be a collection of XML child elements of the section XML element, however, in each
case a property is required in the ConfigurationSection derived class to represent
the ConfigurationProperty instance that will be created by the .Net Framework configuration
system to hold the configuration settings data. Each of the ConfigurationSection
derived class properties that is representing a configuration property should be
decorated with the ConfigurationPropertyAttribute or the ConfigurationCollectionAttribute,
in the case of a collection property, to define the property as a configuration
property wrapper. Any properties that require their values to be verified should
also be decorated with the relevant validation attribute. To demonstrate the declarative
model we will create a configuration section handler to process a configuration
section with several attributes and one child element which also has several attributes,
some of which are optional.
Proposed configuration section to be processed by the declarative
model configuration section handler:
<configuration>
<smtpSettings host="smtp.domain.com" userId="appbox@abusiness.co.uk"
password="........" timeout="60000">
<mailTo to="sysbox@abusiness.co.uk" subject="Contact Us" />
</smtpSettings>
</configuration>
Declarative model configuration section handler:
internal class SmtpSettingsSection : ConfigurationSection
{
public SmtpSettingsSection() : base() { }
[ConfigurationProperty("host", DefaultValue = "smtp.domain.com",
IsRequired = true)]
[RegexStringValidator(@"^\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string Host
{
get { return this["host"] as string; }
set { this["host"] = value; }
}
[ConfigurationProperty("mailTo")]
public MailToSettings MailTo
{
get { return this["mailTo"] as MailToSettings; }
set { this["mailTo"] = value; }
}
[ConfigurationProperty("password", DefaultValue = "........",
IsRequired = true)]
[StringValidator(MinLength = 8, MaxLength = 20)]
public string Password
{
get { return this["password"] as string; }
set { this["password"] = value; }
}
[ConfigurationProperty("timeout", DefaultValue = 100000,
IsRequired = true)]
[IntegerValidator(MinValue = 0, MaxValue = 300000)]
public int Timeout
{
get { return (int) this["timeout"]; }
set { this["timeout"] = value; }
}
[ConfigurationProperty("userId", DefaultValue = "appbox@abusiness.co.uk",
IsRequired = true)]
[RegexStringValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string UserId
{
get { return this["userId"] as string; }
set { this["userId"] = value; }
}
}
The SmtpSettingsSection class extends the ConfigurationSection abstract base class
to provide the section handler for the smtpSettings configuration section shown
above. All of the properties are decorated with the ConfigurationPropertyAttribute
to define them as configuration properties and where applicable one of the configuration
settings validation attributes to enable the base class to initialize the relevant
validation class. All of the properties, bar one, map to the similarly named XML
attributes of the smtpSettings XML element. The exception is the MailTo property
which maps to the mailTo child XML element of the smtpSettings XML element. We need
another class derived from the ConfigurationElement abstract base class to implement
the full functionality of this property as it is a complex type, rather than a simple
or primitive string or integer.

MailToSettings class that provides the MailTo property implementation:
internal class MailToSettings : ConfigurationElement
{
public MailToSettings() : base() { }
public MailToSettings(string to, string subject) : this()
{
To = to;
Subject = subject;
}
[ConfigurationProperty("bcc", DefaultValue = "")]
[OptionalRegexStringValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string Bcc
{
get { return this["bcc"] as string; }
set { this["bcc"] = value; }
}
[ConfigurationProperty("cc", DefaultValue = "")]
[OptionalRegexStringValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string Cc
{
get { return this["cc"] as string; }
set { this["cc"] = value; }
}
[ConfigurationProperty("to", DefaultValue = "sysbox@abusiness.co.uk",
IsRequired = true)]
[RegexStringValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string To
{
get { return this["to"] as string; }
set { this["to"] = value; }
}
[ConfigurationProperty("subject", DefaultValue = "App mail",
IsRequired = true)]
[StringValidator(MinLength = 1, MaxLength = 50)]
public string Subject
{
get { return this["subject"] as string; }
set { this["subject"] = value; }
}
}
The MailToSettings class is similar to the SmtpSettingsSection class, however, it
directly extends the ConfigurationElement abstract base class to provide the properties
of the nested mailTo XML element. The other difference is that two of the XML attributes,
bcc and cc, of the mailTo XML element are optional and therefore may not be included
in the configuration file settings. This presents us with a slight problem because
we need to validate the settings if they are present in the configuration file but
we also need to be able to ignore them and return a default value of empty if they
are not present in the configuration file. To achieve this we need to develop two
more classes, the OptionalRegexStringValidator class that can be used to verify
the settings if they are present but ignore them i.e. not throw an exception, if
they are missing and the OptionalRegexStringValidatorAttribute class that can be
used in the declarative model to provide the base class with the value required
to initialize an instance of the OptionalRegexStringValidator class at runtime.

OptionalRegexStringValidator class that provides optional
regular expression validation:
internal sealed class OptionalRegexStringValidator : RegexStringValidator
{
public OptionalRegexStringValidator(string regex) : base(regex) {}
public override void Validate(object value)
{
string propertyValue = value as string;
if (!string.IsNullOrEmpty(propertyValue))
{
base.Validate(propertyValue);
}
}
}

OptionalRegexStringValidatorAttribute class that provides
declarative initialization of the OptionalRegexStringValidator class:
internal sealed class OptionalRegexStringValidatorAttribute :
ConfigurationValidatorAttribute
{
private string _regex;
public OptionalRegexStringValidatorAttribute(string regex) : base()
{
if (string.IsNullOrEmpty(regex))
{
throw new ArgumentException("Argument cannot be null or empty.",
"regex");
}
_regex = regex;
}
public string Regex
{
get { return _regex; }
}
public override ConfigurationValidatorBase ValidatorInstance
{
get
{
return new OptionalRegexStringValidator(_regex);
}
}
}
The OptionalRegexStringValidator class extends the RegexStringValidator class, which
extends the ConfigurationValidatorBase abstract base class. We need to provide optional
verification, so we override the Validate method and call the base class version
if our override is passed a property value and just let the method return if the
value passed in was null or empty. The OptionalRegexStringValidatorAttribute class
extends the ConfigurationValidatorAttribute base class to provide an attribute that
can be used in the declarative model to create an instance of the OptionalRegexStringValidator
class to validate the configuration property that is decorated with the attribute.
Our first thought when creating the attribute class was to extend the existing RegexStringValidatorAttribute
class, however it is sealed and cannot therefore be extended.
In order to use our proposed smtpSettings configuration section and its associated
handler we need to declare the section and handler. For this sample or demonstration
the configuration section handler and associated classes are contained in the Blayd.Samples
namespace and are located in the same Windows Forms assembly as the code that utilizes
the settings.
Configuration section and section handler declaration:
<configuration>
<configSections>
<section name="smtpSettings" type="Blayd.Samples.SmtpSettingsSection,
ConfigSectionHandlers, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=null" />
</configSections>
<smtpSettings host="smtp.domain.com" userId="appbox@abusiness.co.uk"
password="........" timeout="60000">
<mailTo to="sysbox@abusiness.co.uk" subject="Contact Us" />
</smtpSettings>
</configuration>
To make use of the settings in the smtpSettings configuration
section the code would be similar to the following:
private string _host;
private string _userId;
private string _password;
private int _timeout;
private string _mailTo;
private string _subject;
private string _bcc;
private string _cc;
private void LoadConfigurationSettings()
{
SmtpSettingsSection settings =
ConfigurationManager.GetSection("smtpSettings")
as SmtpSettingsSection;
if (settings!= null)
{
_host = settings.Host;
_userId = settings.UserId;
_password = settings.Password;
_timeout = settings.Timeout;
// Mail to...
_mailTo = settings.MailTo.To;
_subject = settings.MailTo.Subject;
_bcc = settings.MailTo.Bcc;
_cc = settings.MailTo.Cc;
}
}
The configuration section is retrieved in a call to the static ConfigurationManager.GetSection
method and cast to the Type of the section handler class i.e. SmtpSettingsSection.
From there on it could not be simpler, because of the type safety and the default
values provided by the SmtpSettingsSection class, the calling code is not required
to check for null, cast to the required type or to parse the integer. However, in
a real application, we would recommend that you assign default values to the (string)
fields in a constructor to avoid problems if the configuration section cannot be
located in the configuration file.
Programmatic Model Configuration Section Handler
When using the programmatic model to create a configuration section handler we need
to extend the ConfigurationSection abstract base class to implement a handler for
the configuration section XML element. We then need to create a property getter
and optionally a setter for each of the configuration settings in the configuration
section XML element. A configuration setting may be an XML attribute of the section
XML element or it may be an XML child element of the section XML element or it may
be a collection of XML child elements of the section XML element, however, in each
case a property is required in the ConfigurationSection derived class to represent
the ConfigurationProperty instance that we will be creating to hold the configuration
settings data. To demonstrate the programmatic model we will create a configuration
section handler to process a configuration section with several attributes and a
collection of child elements which also have several attributes, some of which are
optional.
Proposed configuration section to be processed by the programmatic
model configuration section handler:
<configuration>
<smtpSettings host="smtp.domain.com" userId="appbox@abusiness.co.uk"
password="........" timeout="60000">
<recipients>
<clear />
<add id="ContactUs" to="admin@abusiness.co.uk" subject="Contact Us" />
<add id="SalesOrder" to="accounts@abusiness.co.uk"
cc="shipping@abusiness.co.uk"
subject="Sales Order" />
<add id="Support" to="support@abusiness.co.uk" subject="Support" />
</recipients>
</smtpSettings>
</configuration>
Programmatic model configuration section handler:
internal class SmtpSettingsSection : ConfigurationSection
{
private static readonly ConfigurationProperty _host;
private static readonly ConfigurationProperty _recipients;
private static readonly ConfigurationProperty _password;
private static readonly ConfigurationProperty _timeout;
private static readonly ConfigurationProperty _userId;
private const string REGEX_EMAIL =
@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
private const string REGEX_DOMAIN = @"^\w+([-.]\w+)*\.\w+([-.]\w+)*";
static SmtpSettingsSection()
{
_host = new ConfigurationProperty("host",
typeof(string), "smtp.domain.com", null,
new RegexStringValidator(REGEX_DOMAIN),
ConfigurationPropertyOptions.IsRequired);
_recipients = new ConfigurationProperty("recipients",
typeof(RecipientCollection), null,
null,
null,
ConfigurationPropertyOptions.IsRequired);
_password = new ConfigurationProperty("password",
typeof(string), "........", null,
new StringValidator(8, 20),
ConfigurationPropertyOptions.IsRequired);
_timeout = new ConfigurationProperty("timeout",
typeof(int), 100000, null,
new IntegerValidator(0, 300000),
ConfigurationPropertyOptions.IsRequired);
_userId = new ConfigurationProperty("userId",
typeof(string), "appbox@abusiness.co.uk", null,
new RegexStringValidator(REGEX_EMAIL),
ConfigurationPropertyOptions.IsRequired);
}
public SmtpSettingsSection() : base()
{
this.Properties.Add(_host);
this.Properties.Add(_recipients);
this.Properties.Add(_password);
this.Properties.Add(_timeout);
this.Properties.Add(_userId);
}
public string Host
{
get { return this["host"] as string; }
set { this["host"] = value; }
}
public string Password
{
get { return this["password"] as string; }
set { this["password"] = value; }
}
public RecipientCollection Recipients
{
get { return this["recipients"] as RecipientCollection; }
}
public int Timeout
{
get { return (int) this["timeout"]; }
set { this["timeout"] = value; }
}
public string UserId
{
get { return this["userId"] as string; }
set { this["userId"] = value; }
}
}
The SmtpSettingsSection class extends the ConfigurationSection abstract base class
to provide the section handler for the smtpSettings configuration section shown
above. When using the programmatic model we have to create the ConfigurationProperty
instance in our own code and in the example above we have declared a set of static
fields to hold the underlying configuration properties, these fields are initialized
in the static constructor. When creating the ConfigurationProperty instance from
code there is bit more work to do compared to the attribute model but we do gain
full control over the process. We add our ConfigurationProperty fields to the collection
managed by the ConfigurationElement base class in the instance constructor. All
of the properties, bar one, map to the similarly named XML attributes of the smtpSettings
XML element. The exception is the Recipients property which maps to the recipients
child XML element of the smtpSettings XML element. This element is mapped to an
add, remove, clear map collection class named RecipientCollection and derived from
the ConfigurationElementCollection class, which holds instances of the RecipientElement
class. The RecipientElement class is derived from the ConfigurationElement abstract
base class and represents the attributes of each of the add child XML elements of
the recipients XML element.

RecipientElement class that provides the add element property
implementation of the recipients element:
internal class RecipientElement : ConfigurationElement
{
private static readonly ConfigurationProperty _bcc;
private static readonly ConfigurationProperty _cc;
private static readonly ConfigurationProperty _id;
private static readonly ConfigurationProperty _to;
private static readonly ConfigurationProperty _subject;
private const string REGEX_EMAIL =
@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
static RecipientElement()
{
_bcc = new ConfigurationProperty("bcc",
typeof(string), "", null,
new OptionalRegexStringValidator(REGEX_EMAIL),
ConfigurationPropertyOptions.None);
_cc = new ConfigurationProperty("cc",
typeof(string), "", null,
new OptionalRegexStringValidator(REGEX_EMAIL),
ConfigurationPropertyOptions.None);
_id = new ConfigurationProperty("id",
typeof(string), "ContactUs", null,
new StringValidator(1, 50),
ConfigurationPropertyOptions.IsRequired |
ConfigurationPropertyOptions.IsKey);
_to = new ConfigurationProperty("to",
typeof(string), "sysbox@abusiness.co.uk", null,
new RegexStringValidator(REGEX_EMAIL),
ConfigurationPropertyOptions.IsRequired);
_subject = new ConfigurationProperty("subject",
typeof(string), "App mail", null,
new StringValidator(1, 50),
ConfigurationPropertyOptions.IsRequired);
}
public RecipientElement(): base()
{
this.Properties.Add(_bcc);
this.Properties.Add(_cc);
this.Properties.Add(_id);
this.Properties.Add(_to);
this.Properties.Add(_subject);
}
public string Bcc
{
get { return this["bcc"] as string; }
set { this["bcc"] = value; }
}
public string Cc
{
get { return this["cc"] as string; }
set { this["cc"] = value; }
}
public string Id
{
get { return this["id"] as string; }
set { this["id"] = value; }
}
public string To
{
get { return this["to"] as string; }
set { this["to"] = value; }
}
public string Subject
{
get { return this["subject"] as string; }
set { this["subject"] = value; }
}
}

RecipientCollection class that provides the Recipients property
implementation and will be populated, by the configuration system, with instances
of the RecipientElement class:
internal class RecipientCollection : ConfigurationElementCollection
{
public RecipientCollection() : base() { }
public RecipientElement this[int index]
{
get { return BaseGet(index) as RecipientElement; }
set
{
if (value == null)
{
throw new ArgumentNullException();
}
if ((index < 0) || (index > Count))
{
throw new ArgumentOutOfRangeException("index",
"Index is out of range.");
}
if (index < Count)
{
// Replace...
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
new public RecipientElement this[string key]
{
get { return BaseGet(key) as RecipientElement; }
}
protected override ConfigurationElement CreateNewElement()
{
return new RecipientElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
string id = string.Empty;
RecipientElement recipient = element as RecipientElement;
if (recipient != null)
{
id = recipient.Id;
}
return id;
}
public void Add(RecipientElement element)
{
if (element == null)
{
throw new ArgumentNullException();
}
BaseAdd(element);
}
public void Clear()
{
BaseClear();
}
public int IndexOf(RecipientElement element)
{
return BaseIndexOf(element);
}
public void Remove(RecipientElement element)
{
if (element != null)
{
BaseRemove(element.Id);
}
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
}
The RecipientElement class, which extends the ConfigurationElement base class initializes
its static ConfigurationProperty instances in the static constructor and makes use
of the OptionalRegexStringValidator class that was developed for the declarative
or attribute model example shown earlier in this article. The RecipientCollection
class extends the ConfigurationElementCollection abstract base class to represent
the recipients XML element in the configuration file. The ConfigurationElementCollection
class also extends the ConfigurationElement base class which includes its own string
indexer, therefore, we have to hide the base class property by declaring the RecipientCollection
string indexer as new. Other than that there is nothing particularly remarkable
about either class, what is clever, however, is how the .Net Framework configuration
system puts all of this together to load and parse the configuration settings, create
the collection instance and then populate it with instances of the RecipientElement
class.
In order to use our proposed smtpSettings configuration section and its associated
handler we need to declare the section and handler. For this sample or demonstration
the configuration section handler and associated classes are contained in the Blayd.Samples
namespace and are located in the same Windows Forms assembly as the code that utilizes
the settings.
Configuration section and section handler declaration:
<configuration>
<configSections>
<section name="smtpSettings" type="Blayd.Samples.SmtpSettingsSection,
ConfigSectionHandlers,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=null" />
</configSections>
<smtpSettings host="smtp.domain.com" userId="appbox@abusiness.co.uk"
password="........" timeout="60000">
<recipients>
<clear />
<add id="ContactUs" to="admin@abusiness.co.uk" subject="Contact Us" />
<add id="SalesOrder" to=accounts@abusiness.co.uk
cc="shipping@abusiness.co.uk" subject="Sales Order" />
<add id="Support" to="support@abusiness.co.uk" subject="Support" />
</recipients>
</smtpSettings>
</configuration>
To make use of the settings in the smtpSettings configuration
section the code would be similar to the following:
private string _host;
private string _userId;
private string _password;
private int _timeout;
private void LoadConfigurationSettings()
{
SmtpSettingsSection settings =
ConfigurationManager.GetSection("smtpSettings")
as SmtpSettingsSection;
if (settings!= null)
{
_host = settings.Host;
_userId = settings.UserId;
_password = settings.Password;
_timeout = settings.Timeout;
foreach (RecipientElement element in settings.Recipients)
{
Console.WriteLine("Mail to: {0}", element.To);
Console.WriteLine("Subject: {0}", element.Subject);
Console.WriteLine("BCC: {0}", element.Bcc);
Console.WriteLine("CC: {0}", element.Cc);
Console.WriteLine();
}
}
}
The configuration section is retrieved in a call to the static ConfigurationManager.GetSection
method and cast to the Type of the section handler class i.e. SmtpSettingsSection.
From there on it could not be simpler, because of the type safety and the default
values provided by the SmtpSettingsSection class, the calling code is not required
to check for null, cast to the required type or to parse the integer. However, in
a real application, we would recommend that you assign default values to the (string)
fields in a constructor to avoid problems if the configuration section cannot be
located in the configuration file. In the above example, we have simply output the
recipient details to the console because, in a real application, we would not usually
store this type of data again in instance fields, it makes more sense to retrieve
the details of the relevant recipient as and when it is needed.
Conlusion
The configuration section handler classes that ship with the .Net Framework are
fine if your configuration requirements are straight forward, indeed the AppSettingsSection
handler for the appSettings configuration section may be all you need in a lot of
situations. If, however, you need to store more complex configuration settings data
you will almost certainly have to develop your own handler classes. The ConfigurationElement,
ConfigurationSection and the other associated base classes available as from version
2.0 of the .Net Framework make the development of custom configuration section handlers
much easier than it was in earlier framework releases. You can, for example, get
a long way with the declarative or attribute model which may save you quite a lot
of coding and if you require more flexibility or control you have the option of
the programmatic model. However, whichever strategy you choose, you may well end
up with a lot of configuration related classes in your solution, so it make sense
to consider the design of your configuration settings carefully and to try to develop
handlers that can be used in more than one solution.
Copyright ©Blayd Software Limited 2008-2010. All rights reserved.