Read and write settings using custom schemes.
TECHNOLOGIES: Section Handler | Profile | web.config | Configuration
Manage Configuration Data
Read and write settings using custom schemes.
By Dino Esposito
The collection of web.config files that characterize an ASP.NET application provides the runtime with configuration data affecting the way every URL is processed. All the web.config files are written according to the same XML schema. They can contain settings that rule the application and the system interaction, as well as application-specific settings and parameters. All the subtrees in a web.config file play specific roles and contain particular chunks of information: the parameters of the process model, the installed HTTP modules, and details of the authentication and authorization scheme. Custom configuration data for the application is written under the <appSettings> section. But to be stored in the section, the data must be in key/value format. This requirement might be too strict, so in this article, I'll examine ways to store and manage custom application data for ASP.NET applications.
This code snippet shows a simple web.config file that contains the application's connection string:
<add key="ConnectionString" value="..." />
The syntax of the <appSettings> section is defined like this:
<add key="..." value="..." />
<remove key="..." />
The <add> element adds a new key/value pair to the internal collection; the new setting has a value and is identified by a unique key. The <remove> element removes the specified setting from the collection; the setting is identified using the key. Finally, the <clear> element clears all the settings defined in the section.
The connection string is typical application-specific information you might want to store in web.config. It is only an example, though; storing such a critical argument as clear text is not considered a safe practice and is strongly discouraged. The .NET Framework provides ad-hoc tools to read the information stored in web.config and in all configuration files for the various models of application:
conn = ConfigurationSettings.AppSettings["ConnString"];
The ConfigurationSettings class is defined in the System.Configuration namespace and provides access to settings in a specified configuration section. The AppSettings property of the class is a read-only NameValueCollection object designed to get the information stored in the <appSettings> section. The fact that AppSettings is rendered as a NameValueCollection object is not coincidental and stems from this section declaration in machine.config:
Application settings are read by a class named NameValueFileSectionHandler. It reads the XML source and packages it as key/value pairs. If you find the built-in scheme too restrictive for the data you plan to store, the most viable workaround is to add a custom section and write your own section handler. I'll explain that approach in detail in a moment. In the meantime, consider this possibility: overriding the appSettings section in the application's web.config:
<remove name="appSettings" />
type="NewSectionHandler, Assembly" />
The previous declaration removes the current definition of the <appSettings> section first, then adds a new one bound to a different handler. The actual contents of the section are determined by the format the handler can manage.
NameValueFileSectionHandler is the class registered as the reader of the data in the <appSettings> section. The .NET Framework documentation skips over it and sets limits to note that the class is intended for internal use only. NameValueFileSectionHandler actually is a wrapper for the NameValueSectionHandler class and provides an extra feature that, although functional in ASP.NET 1.0, has been documented fully only with version 1.1. In particular, the section handler named NameValueFileSectionHandler allows the application settings to be stored in a separated file in accordance with this syntax:
<appSettings file="myfile.config" />
The content of the file pointed to by the file attribute is read as if it is an <appSettings> section in the configuration file. Note that the file's root element must match <appSettings>. Using external files is advantageous because any changes, although effective for the application, do not touch web.config and do not cause all the pages to recompile. ASP.NET, in fact, detects any changes to any web.config files and voids all the dynamic assemblies created previously. Of course, this is far from being a serious problem as long as you enter changes to web.config sporadically. By contrast, if saving changes to web.config is the result of a precise feature of the application, automatic obsolescence of assemblies becomes a relevant drawback.
The NameValueFileSectionHandler object processes the contents of the embedded file using the NameValueSectionHandler class. If no file is embedded in the <appSettings> section but the settings are added using the <add> element, the two section handlers are functionally equivalent.
Persist Application Settings
ASP.NET pages might need to persist some settings on the server, though not as frequently as desktop applications. Because machine.config should never be touched, there are three possibilities: use the <appSettings> section of the application's web.config, use a custom XML file, or store information in a database. The best option depends on a number of factors, including the amount of information to write, the API used to write it, security implications, and the speed of retrieval.
By default, an ASP.NET application has no permission to write to any server-side file, either existing or to be created, and web.config is no exception. (Note that, by default, only users in the Administrators group can edit web.config.) To modify web.config programmatically, you must change the security settings of the web.config file to enable full control by the account running the ASP.NET application.
So, you can overwrite web.config programmatically - but is it worthwhile? The first consideration is that web.config is kept in memory, so the leaner you keep the file, the better. More importantly, any change to the file provokes all the pages affected by that copy of web.config to be recompiled on the next access. If the key/value scheme supported by <appSettings> suits your needs, link it to an external file. In any case, you must grant the ASP.NET account rights to write to the file, but at least changes don't affect web.config and don't cause recompilation. You don't need to change the API to access information in an external file. Whether you embed <appSettings> in web.config or move it to a linked file is completely transparent to the code. This code does the same thing, irrespective of the physical location of the requested application setting:
conn = ConfigurationSettings.AppSettings["ConnString"];
The logic necessary to detect and handle the different situations is buried in the code of the NameValueFileSectionHandler. In summary, as long as the scheme of <appSettings> fits your bills, you have no reason not to use an external file.
There are other pros and cons to using databases or custom XML files. For instance, you are not limited to the key/value scheme, but you can't rely on an existing and very specific API to query data. Also, you can rely on ADO.NET and XML readers, respectively, but you must write yourself a more specific API comparable to the ConfigurationSettings object.
Note also that the .NET Framework configuration API doesn't supply writing methods. So, to persist data, you must resort to ADO.NET, XML writers, or, better yet, the XML Document Object Model (XMLDOM) API.
What if you need more flexibility to express the relationships within your custom data? In this case, XML files can be more helpful than databases or flat key/value pairs. After creating your own schema, though, you must find a software layer to interact with the data in reading and writing. A solution based on XMLDOM is a good choice and covers both reading and writing. But, at least for reading custom configuration data, there's a better approach: Use a custom section handler.
Create New Configuration Sections
Suppose you want to store enough information in a configuration file to create a collection of links for the page header. Each physical hyperlink is characterized by a URL, a display name, and possibly a tool tip. You need a table row or a custom class to represent it fully. Subsequently, a collection of links can be rendered through an ADO.NET DataTable object easily. In this version of the .NET Framework, no section handler object is capable of reading a DataTable object out of a configuration file. Let's see what you need to create one using a DataSet class instead.
A section handler reads data from a section of web.config. A DataSet section handler reads data stored in a format compatible with the XML representation of a DataSet object. The DatasetSectionHandler class implements the IConfigurationSectionHandler, which consists of a single method named Create:
The XML data to be parsed is passed via an XmlNode object - that is, an object that represents the root of an XML DOM subtree. To enable a DataSet object's ReadXml method to parse an XML DOM subtree , you must wrap the subtree in an XmlNodeReader object - an XML reader object capable of reading the .NET Framework implementation of the XML DOM. Figure 1 shows the full source code for the DataSet section handler.
DatasetSectionHandler : IConfigurationSectionHandler
public object Create(object parent, object context,
// Clone the parent DataSet if not null
if (parent == null)
ds = new DataSet();
ds = ((DataSet) parent).Copy();
// Read the data using a node reader
DataSet tmp = new DataSet();
nodereader = new XmlNodeReader(section);
// Merge with the parent and return
Figure 1. The DatasetSectionHandler class implements the IConfigurationSectionHandler class and provides only a method named Create. The method receives the XML text to process via an XmlNode object.
Any section registered with this handler must contain XML text compliant with the XML schema of DataSet objects. To define a custom section in web.config, you resort to the <configSections> initial section and define a <sectionGroup> and a <section>. Notice that the <sectionGroup> element is optional but strongly recommended to avoid name conflicts. The attribute type of <section> indicates which of the section handler's class name and assembly to use (see Figure 2).
Figure 2. The custom Links section defines the characteristics of the data through the section handler component. Later in the web.config file, you find the actual data. In this case, it's the XML representation of a dataset.
The actual data finds its place within the AspNetPro/Links subtree. You use the ConfigurationSettings class's GetConfig method to retrieve this information programmatically:
void Page_Load(object sender, EventArgs e)
o = ConfigurationSettings.GetConfig("AspNetPro/Links");
DataSet ds = (DataSet) o;
In the previous code snippet, the BuildPageHeader procedure is a simple placeholder representing any code that works with the extracted data. To insert custom data in a web.config file, you don't strictly need to create a custom section. As I mentioned earlier, you can override the definition of the <appSettings> section and make its contents comply with a completely different scheme. As long as you write a section handler, you can exploit the official .NET API to read custom data out of configuration files. Writing configuration data is another story. The .NET Framework doesn't offer any facilities, so you must do it all yourself the way that best suits your needs.
The code in this article is available for download.
Dino Esposito is a trainer and consultant who specializes in ASP.NET, ADO.NET, and XML. Author of Building Web Solutions with ASP.NET and ADO.NET and the upcoming Programming ASP.NET (both from Microsoft Press), Dino also is the co-founder of http://www.VB2TheMax.com. E-mail him at mailto:firstname.lastname@example.org.
Tell us what you think! Please send any comments about this article to email@example.com. Please include the article title and author.