AddThis Social Bookmark Button

Print

Beginnings of a Flexible .NET Architecture: A Simplified Config Service

by Satya Komatineni
08/19/2002

Every application needs to read configuration information at run time. This need is well recognized under .NET, which provides a unified XML-based configuration file. For ASP.NET, this file is called web.config. Your application will be able to read the contents of this file at run time. .NET provides a series of calls making this process fairly simple; nevertheless you will need to write some C# code to accomplish this. My proposed solution simplifies this process further, and provides a layer of abstraction largely independent of the XML format of your file(s).

In addition, the programmer needs know very little about the XML classes and other XML details. A "configuration service" is the beginning of a flexible architecture. It is a string that, when pulled in the proper manner, will gradually lead to the following services:

  • Unified factory service
  • Relational data access services
  • Declarative, hierarchical, infoset-based dataset services
  • Declarative component services

At some point in the future, I may explain in detail how this transition could take place. In this article I may allude to a combination of these services as I explain the configuration service.

Related Reading

.NET Framework Essentials
By Thuan L. Thai, Hoang Lam

Let's start by looking at a sample web.config file with some configuration entries.


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <configSections>
     <section name="SimpleConfiguration"
              type="MySectionHandler,MyAssembly">
     </section>
   </configSections>

<SimpleConfiguration>
  <Module1>
    <section1>
      <key1>value1</key1>
      <key2>value2</key2>
    </section1>
  </Module1>
  <Module2>
    <section1>
      <key1>value1</key1>
      <key2>value2</key2>
    <section1>
  </Module2>
</SimpleConfiguration>

What Does .NET Do With This File?

While putting together the above configuration file, your intention is to read the entries in the SimpleConfiguration section. To accomplish this, you do the following in .NET:


ArrayList modules =
(ArrayList)System.Configuration.ConfigurationSettings.
    GetConfig("SimpleConfiguration");

There are a few things happening in this line. When the above call is issued, .NET will invoke a class identified by SimpleConfiguration (in the configSections) and call its well-known Create method by passing an XML node to that create method. Whatever object this Create method returns will be handed to the caller of GetConfig(...). Just to be a bit more complete, here is sample code for one such handler:


public class MySectionHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, 
XmlNode section)
   {
      ... Read your xmlnode section
      ... return an ArrayList
   } // end of method
} // end of class

There must be an understanding between the client and the section handler that they are dealing with the same kind of object. In this case, this object is ArrayList.

How Can This Approach Be Simplified?

There are a few nice things about the above approach. The caller will get a C# object for each XML section. And you can have any number of XML sections. On the other hand, for such convenience you will have to:

  1. Write a class for each section.
  2. Parse your XML section to convert it into suitable objects.

Often, clients want a much simpler interface to their configuration. Here is an example:


string value = AppServices.getValue(
"/SimpleConfiguration/Module1/section1/key1");

Once we have such an API available, there is no longer any need for creating new section handlers -- at least for the simple configuration stuff. Although I call this "simple stuff," it is quite amazing how far you can take this simplicity. But first, let us tighten up this API.

Often, you will see that some configuration keys are mandatory. Keeping that in mind, AppServices.getValue(...) will throw an exception if a key is not found. Also, the client might want to specify a default value in case a certain key is not found. To take this into account, here is the overloaded version of that API.


string value = AppServices.getValue(
"/SimpleConfiguration/Module1/section1/key1",
"defaultvalue");

This version of the API will not throw an exception if the key is not found, but instead will return the default value that is passed in. For example, you can also do the following:


string value =
AppServices.getValue(
"/SimpleConfiguration/Module1/section1/key1", null);
if (value == null)
{
    // you have just avoided an exception if you care to do so
}

It is also possible to make the keys case-insensitive by reducing the error rate; this approach is quite similar to Xpath. Recognizing that, here is another version of this API:

string xpathExpression = ".";
string value = AppServices.getXPathValueString(
xpathExpression);

Occasionally you may have multiple nodes at a given key, in which case you may want to receive the node as an XML node yourself. You may want to do the following:


XMLNode node = AppServices.getXPath(xpathExpression);

Let me come back to the basic AppServices.getValue(key). Sometimes, although the key is specified in an XML file, its contents might be white space. This may be considered the same as not mentioning the key at all. And as such, one might expect the API to throw an exception. To accommodate this variation, one can call another function:


string value = AppServices.getValueHonourWhiteSpace(key);

In summary, here are the functions we have so far:


Public class AppServices
{
    // Configuration Service
    public static getValue(string key);
      // throws an exception if the key is not found or has an 
     // empty string
    public static getValueHonourWhiteSpace(string key)
      // throws an exception if the key is not found

    public static getValue(string key, string default);
      // returns the default if the key is not found or has an 
     // empty string
    public static getValueHonourWhiteSpace(string key,string default)
      // returns the default if the key is not found

    //Xpath support
    public static getXPathValue(string key);
       // throws an exception if the key is not found or has an 
      // empty string
    public static getXPathValueHonourWhiteSpace(string key)
      // throws an exception if the key is not found

    public static getXPathValue(string key, string default);
      // returns the default if the key is not found or has an 
      // empty string
    public static getXPathValueHonourWhiteSpace(string key, string default)
     // returns the default if the key is not found

    // Other future services
}

What we are striving for here is simplicity. One may choose to implement a portion of the above APIs.

Pages: 1, 2, 3

Next Pagearrow