Configuration sections add to the usefulness of an
application, whether it is a web or windows application, or even a custom
library of components. Configuration sections are portions of the configuration
file that represent specific settings for the application that it represents. I
am not going to go into too much detail regarding them; for more information,
please consult
the following article.
The syntax for declaring properties of a custom
configuration section, or a configuration element, can be tedious. Look at the
following example.
Listing 6
[ConfigurationProperty("redirectUrl", DefaultValue = "default.aspx")]
public string RedirectUrl
{
get
{
return (string)this["redirectUrl"];
}
set
{
this["redirectUrl"] = value;
}
}
The above example is a property of a configuration section. To
expose the property, the ConfigurationProperty attribute declares the name that
will appear as an attribute, along with additional criteria (in this example, a
default value to use when a value is not provided).
In addition, configuration sections use a collection of
properties, which have a type of object. Casting the value to the appropriate
type is a requirement. It is not difficult to do, but there is a way to make it
easier. To start, the script uses two properties to define the namespace and
name of the class.
Listing 7
<%@ Property Name="ClassName" Type="String" Default="" Optional="False" %>
<%@ Property Name="ClassNamespace" Type="String" Default="Nucleo.Configuration"
Optional="False" %>
This approach uses an XML file as the source to illustrate
another approach for storing data. Rather than hard-coding the file name, I
chose to use the FileNameEditor, which is a file-browsing editor that allows me
to navigate to a file in a graphical window. This object resides in the System.Windows.Forms
assembly. The definition to use this approach is shown below.
Listing 8
<%@ Property Name="ConfigurationFile" Type="String" Default="" Optional="False"
Editor="System.Windows.Forms.Design.FileNameEditor, System.Design,
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>
To add file selection editing capabilities, one simply needs
to reference the Design assembly using the full name. These properties store
what we are calling the object,and where to get the data. What does the data
look like? Below is a single entry in the XML file.
Listing 9
<Property>
<Type>bool</Type>
<Name>UseThat</Name>
<IsRequired>true</IsRequired>
</Property>
This property element is wrapped by a parent element, and
mimics the structure that a dataset uses to read/write XML. Note the three
properties: type (of data), name, and whether the field is required. All of
this will affect the output. The script gets the data by reading the XML into a
DataSet object and assigning the table locally.
Listing 10
private void ReadFile()
{
DataSet propertiesData = new DataSet();
propertiesData.ReadXml(this.ConfigurationFile);
_propertiesTable = propertiesData.Tables[0];
}
This method is triggered to run at the beginning of the
script. Following the download of the data, the script continues by rendering
the base shell.
Listing 11
using System;
using System.Configuration;
namespace <%= this.ClassNamespace %>
{
public class <%= this.ClassName %> : ConfigurationSection
{
}
}
Next, each of the properties renders using information from
the DataTable object that holds all the property data. It is read from as shown
below.
Listing 12
<%
foreach (DataRow row in _propertiesTable.Rows)
{
string propertyType = row["Type"].ToString();
string propertyName = row["Name"].ToString();
bool isRequired = bool.Parse(row["IsRequired"].ToString());
%>
[ConfigurationProperty(<%= this.GetConfigurationName(propertyName) %>,
IsRequired=<%= isRequired.ToString() %>)]
public <%= propertyType %> <%= propertyName %>
{
get
{
return (<%= propertyType %>)
this["<%= this.GetConfigurationName(propertyName) %>"];
}
set { this["<%= this.GetConfigurationName(propertyName) %>"] = value; }
}
<%
}
%>
Note the <% %> tags; this is the server-side scripting
portion that sets up a for loop to access through each of the properties, gets
a local variable reference, appends the ConfigurationProperty attribute,
declares the getter and setter, and makes sure all the assignments are correct.
Also note that the GetConfigurationName method simply converts the first letter
of the word to lower case.
This is a pretty simple script that renders the following.
Listing 13
using System;
using System.Configuration;
namespace Nucleo.Configuration
{
public class SomeSection: ConfigurationSection
{
[ConfigurationProperty(useThat, IsRequired = True)]
public bool UseThat
{
get
{
return (bool)this["useThat"];
}
set
{
this["useThat"] = value;
}
}
[ConfigurationProperty(useThis, IsRequired = False)]
public bool UseThis
{
get
{
return (bool)this["useThis"];
}
set
{
this["useThis"] = value;
}
}
}
}
Though not complicated to setup manually, this template
helps create longer configuration sections rather easily. I have had
configuration sections that this was useful for.