Welcome to the next installment of the .NET Nuts & Bolts column. This article covers using application configuration files in Microsoft .NET. It briefly outlines the concept of application configuration files and touches on the native support the Microsoft .NET Framework provides. In particular, it discusses using the System.Configuration namespace among others within the Framework.
Application Configuration Background
Building configurable applications is not a new concept. The idea of applications relying on configuration parameters to dictate behavior at run time has been around for many years. For those of us who were around Microsoft-based programming prior to .NET, the most common example is the use of INI files. INI files are simple text-based files with key and value pairs; the key is used to fetch the value, and the value is then used in some way to influence the application settings and/or resulting behavior. This way, you can modify the configuration file to drive program behavior without having to recompile the application.
INI files generally worked great, except for the following drawbacks:
- Everyone wrote their own parsers for dealing with them.
- You had to have them on the machine where the application was running.
- They required disk operations to read their files.
Microsoft moved away from INI files and towards storing everything in the Registry, which made application deployment much more difficult. As a result, Microsoft moved back to the concept of an “xcopy deployment,” by which programmers deployed applications by simply copying files. Redmond used XML and some objects built into the Microsoft .NET Framework to bring new life to our old friend the application configuration file.
The System.Configuration namespace provides the functionality for reading configuration files. Microsoft released an XML schema dictating the format for configuration files that can be read using the System.Configuration API, enabling the accessing object to automatically consume the configuration files. This way, you can allow configuration files to be read without having to develop a bunch of plumbing to read the file and find the desired setting. Multiple types of configuration files are relevant to .NET, but this article focuses exclusively on the configuration files containing application-specific settings.
The name and storage location of an application configuration file depends on the application type with which it is being used. A configuration file for an executable (.exe) is located in the same directory as the application. The file is the name of the application with a .config extension. For example, notepad.exe would have a configuration file of notepad.exe.config. A configuration file for an ASP.NET application is called web.config and is located in the root of the application’s virtual directory.
An application configuration file follows a specific XML schema. The appSettings section is a predefined section of the configuration file designed to make it very easy to retrieve a value based on a given name. This is the easiest way to add application-specific settings into an application configuration file. The appSettings section of the configuration file consists of a series of “add” elements with “key” and “value” attributes. While the appSettings section is predefined, it is not included in a configuration file by default and must be manually added. A simple example of a configuration file would be the following:
<?xml version="1.0" encoding="utf-8" ?><configuration> <appSettings> <add key="ApplicationTitle" value="Sample Console Application" /> <add key="ConnectionString" value="Server=localhost;Database=Northwind;Integrated Security=false;User Id=sa;Password=;" /> </appSettings></configuration>
The AppSettings property of the ConfigurationSettings object in the System.Configuration namespace is used to get the settings. For example, to read either of the settings above, the following sample code would do the trick:
// Read appSettingsstring title = ConfigurationSettings.AppSettings["ApplicationTitle"];string connectString = ConfigurationSettings.AppSettings["ConnectionString"];
Now, you could easily set a dynamic title value or read database connection string information from the application configuration file. This gives you the freedom to adjust settings without having to recompile any code.
Customized Configuration Sections
If you have distinct groupings of configurations to be included in the application configuration file, consider creating a custom configuration section in lieu of the appSettings section. This will allow you more organizational structuring around the storage of various settings. You include a custom section by including the configSections element in the configuration file and a sub-element called section that defines the custom section and the handler for reading it. The .NET Framework contains an IConfigurationSectionHandler interface that dictates the required functionality handlers provide by allowing you to use custom handlers with the Configuration if you so choose. This example just uses handlers the .NET Framework already provides. Continuing the example from the previous section, the following configuration file contains a custom section and specifies which handler to use for reading:
<?xml version="1.0" encoding="utf-8" ?><configuration> <configSections> <section name="sampleSection" type="System.Configuration.SingleTagSectionHandler" /> </configSections> <sampleSection ApplicationTitle="Sample Console Application" ConnectionString="Server=localhost;Database=Northwind; Integrated Security=false;User Id=sa; Password=;" /> <appSettings> <add key="ApplicationTitle" value="Sample Console Application" /> <add key="ConnectionString" value="Server=localhost;Database=Northwind; Integrated Security=false;User Id=sa;Password=;" /> </appSettings></configuration>
The following code reads the configuration settings into a dictionary, where you can easily access the values:
// Read customSectionSystem.Collections.IDictionary sampleTable = (IDictionary) ConfigurationSettings.GetConfig("sampleSection");string title = (string)sampleTable["ApplicationTitle"];string connectString = (string)sampleTable["ConnectionString"];
Customized Configuration Sections and Groups
The issue with custom configuration sections alone is that they rely on attributes to store all of the settings as a part of a single element. This can become cumbersome when a large number of configurations are contained within a section or additional related sections. You can further organize similar custom sections into a section group, which makes it easier to work with numerous configurations and helps eliminate naming conflicts with custom configuration sections. This is especially useful where your application contains reference to several other DLLs that read configuration information from the host applications configuration file. A sectionGroup element is simply placed around one or more section elements to create a group. Expanding the example configuration file, the following configuration file contains a custom group, section, and the pre-defined appSettings:
<?xml version="1.0" encoding="utf-8" ?><configuration> <configSections> <section name="sampleSection" type="System.Configuration.SingleTagSectionHandler" /> <sectionGroup name="CodeGuru"> <section name="utilitySection" type="System.Configuration.NameValueSectionHandler, System,Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </sectionGroup> </configSections> <sampleSection ApplicationTitle="Sample Console Application" ConnectionString="Server=localhost;Database=Northwind; Integrated Security=false;User Id=sa; Password=;" /> <CodeGuru> <utilitySection> <add key="ApplicationTitle" value="Sample Console Application" /> <add key="ConnectionString" value="Server=localhost;Database=Northwind; Integrated Security=false;User Id=sa; Password=;" /> </utilitySection> </CodeGuru> <appSettings> <add key="ApplicationTitle" value="Sample Console Application" /> <add key="ConnectionString" value="Server=localhost;Database=Northwind; Integrated Security=false;User Id=sa;Password=;" /> </appSettings></configuration>
Note the extra “Version,” “Culture,” and “PublicKeyToken” specified in defining a section group. These all come from what the machine.config configures on the local machine. Occasionally, it has worked for me without them, but other times an exception is thrown that it can’t create the appropriate handler. I’ve found that if I include those items, it consistently works.
The following code can access the items in the custom group by loading the group into a NameValueCollection for easy access to the key and value pairs:
// Read custom sectionGroupNameValueCollection collection = (NameValueCollection) ConfigurationSettings.GetConfig("CodeGuru/utilitySection");string title = collection["ApplicationTitle"];string connectString = collection["ConnectionString"];
Note the path-like statement required to find the appropriate section to read when accessing the custom section.
This article has touched on some of the basics of using application configuration files, including the ability to define your own sections and groups of sections. Here are a couple of additional items for you to ponder about application configuration settings:
- Introduce some form of encryption to keep configuration settings safe. While the application configuration files are secured by operating system permissions, that won’t always prevent prying eyes from seeing the information they contain. The use of plain text-based XML makes it insecure to store items such as connection string information that contain passwords within the configuration file. Building your own wrapper around the System.Configuration or creating a custom configuration section and implementing the IConfigurationSectionHandler interface in a custom handler allows you to build your own methods to include encryption to keep information secure in the application configuration file.
- Use application configuration settings to dynamically assign field and/or property values. This won’t work for standalone DLLs that don’t have a configuration file, but it will work if your DLL is utilized by an executable or ASP.NET application, as is often the case with utility libraries or business logic components.
The topic of the next column is yet to be determined. If you have something in particular that you would like me to explain, e-mail me at firstname.lastname@example.org.
About the Author
Mark Strawmyer, MCSD, MCSE, MCDBA is a Senior Architect of .NET applications for large and mid-size organizations. Mark is a technology leader with Crowe Chizek in Indianapolis, Indiana. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C#. You can reach Mark at email@example.com.