KnowDotNet

Advanced Cofiguration Section Manipulation - Part II

by William Ryan

A while ago, I wrote Advanced Configuration Section Manipulation and posted a similar article on my blog.  I got a surprising amount of feedback but I think this comment typifies the majority of responses:

"You'll probably want to slap me for this, but I've never seen the big deal with config files. I mean sure, they're basically like an INI file that handles .NET properties and tells .NET how to run your code, but besides that I don't see any point....

If there was a way to simply convert a config file into binary, I'd probably use them more often. My luck there probably is one, but because I don't like the text based crap to begin with I haven't tried to do homework to see if there is. Even so that could mean that someone could make a decryptor program that could easily decrypt your config file into text. The goal is to make it so that no one can enter the text equivalent because that would lead to a lot of potential problems and tests that I'm just not interested in making any more. I decided back in '97 when I was still in college that text based configurations are the work of the devil and I'm not about to bow to it's wishes even if the .NET god tells me I must obey."

Well, to be completely honest, I think I had the same impression when I first came across .config files.  However there's a lot more than meets the eye here and hopefully with a good Use Case and an implementation maybe I can shed a little light on why I think they're so helpful.
In modern distributed environments, a lot of stuff that used to 'work in a pinch' just won't cut it anymore.  However, it's quite tempting to go old school and do what you always did, because, for one thing you already know how to do it.  But what happens a lot is that increasingly, the time you spend working AROUND these types of solutions starts to get in the way of doing your job.  You have to write up a tremendous amount of documentation teaching people how to set things up and make them work.  You spend more and more time troubleshooting arcane issues that you didn't anticipate.  Users get frustrated, you get frustrated, management gets frustrated, it's lose lose for everyone. I heard it described quite eloquently as "What used to work great starts to crumble under its own weight".  Let me walk through a perfectly illustrative scenario.
Let's say you were in an environment where your client app, (web or windows) needed to ALWAYS be available to the users.  This means that even if the database is down for instance, your application can still run and just do its catch up work when things are brought back up.  Right off the bat, traditional client/server applications aren't going to cut it because obviously if the database is down you're going to have some troubles.  Sure, you can write some code to handle the fact that the db isn't available and write the information elsewhere, but why?  Why not write that information elsewhere in the first place and let another component handle the database communication.  The first thing that comes to mind is MessageQueuing.  Instead of dealing with direct data access like we used to, you can take a more Service Oriented Architecture style approach.  In doing so you build components that send messages to one another so that all any one component needs to deal with is the facade of another component.  The implementation can be hidden behind the facade.   If you think about it, this approach is the logical maturation of object oriented principles that are prevalent today  - just abstracted up one layer.  
Ok, so let's say that we have WinClientApp that accepts payments from customers.  When a payment is accepted, it's sent to a MessageQueue  after being validated by the business logic of the application.  Now, once the message is sent to the queue, the queue can immediately send back an acknowledgement and the customer can go on their way.  If the database that the information ultimately needs to be stored in is unavailable, things can still run.  Now obviously if there db isn't up then the whole process won't be completed, but at least now you can keep things moving -  keep processing people and providing receipts.  Otherwise, you'd have to wait until the DB came back up - standing idly the whole time.  So, you've decided that you are going to use Queues for this application.  This is where .Config files truly shine.  Let's say that you may have one queue, or 500, and that number will change.  Perhaps you need to send the message of payment to multiple government agencies, and those requirements shift over time.  How do you tell your application what queues to use?
Well, you could go really old school and use .ini files.  However, if you may have one queue or 500, that could get ugly quickly because you don't know in advance what you want to read.  You can setup some scheme to handle this but .ini files aren't really intended for this type of dynamic manipulation.  Now, let's throw in another monkey wrench, let's say that you may have some private and some public queues, which behave fundamentally differently in the way your client application deals with them.  You'd have to jump through some hoops to get this working correctly, and it would be error prone at best.
The other old school approach might be to store the information in a database.  You could create a collection of Queue classes that mapped to a database table, query the db, iterate through the returned rows and populate your collection.  Two problems arise here.  The first is that your app isn't talking to the db directly.  So you'd have to configure a separate type of message for configuration and write handler objects to process these messages separately.  The more troubling problem is that if the database is down when a user begins a session, how will they get the configuration information?  Easy, they won't.  No, you could come up with some clever solution that has a secondary file stored locally and in case the db isn't available, use it, but this is more of the same.
What do I mean by "more of the same"?  Well, the approach you are using has inherent flaws in it and you come up with a way to fit the square peg in the round hole.  Often time this force fitting of a solution is a result of pragmatism or necessity but over time, these types of approaches become more and more costly.  While the maintenance costs of dealing with one more of the same approach are relatively low, they tend to increase dramatically over time.
Let's look at a slightly different implementation.  We'll define a business api as well as an interface api to handle reading of the queues.  We'll use the interface API so that these assemblies can be remotable and thereby be hosted on separate application servers if necessary.  Next, we'll define a class to hold our information and another handle the reading.  There are many ways to go about handling this second part, but for the sake of showing a little more complex implementation, we'll use Attribute based XML coupled with a collection class.
First, the configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
       <section name="messageQueues"     type="KDN.QueueInfoCollection, KDNMessageConfigGlobal"/>
    

    <messageQueues>
       <messageQueue Description="InfoConfig"  queueName=".\\MessageQueues\\InfoQuePrimary"/>
       <messageQueue Name="InfoConfigBackup" queueName=".\\MessageQueues\\InfoQueBackup"/>      
    
<system.runtime.remoting>
      <application name="KDNApplicationMessaging">
         <channels>
            <channel ref="http" useDefaultCredentials="false">
               <clientProviders>
                  
              
             <serverProviders>
                  
                  
            
            
        
         <client url="http://localhost:80/KDNMessagingServer">
            <wellknown <BR>                url="http://localhost:80/KDNApplicationMessagingServer/MessageTransmitter.rem"
               type="KDN.Business.IMessageTransmitter, KDNMessageTransmissionAPIInterfaces"/>        
        
      
  


Now, a lot of what I've done here is stuff I've covered in previous articles.  The first part is the <configSections> tag which simply defines a custom configuration section name messageQueues.  I named the configuration section messageQueues and you see it appear right below the configSection declaration.  I reference a type KDN.QueueInfoCollection in an assembly named KDNMessageConfigGlobal.  The names don't matter at all, as long as they match the real object and assembly <gasp>.  Now, in messageQueues you'll notice that unlike the example I used in the first article, I'm not used element based XML - rather I'm using Attribute based XML.  So what?  Well, this allows me to define a more complex structure than a simple single name paradigm - I can use as many different properties as I want.  Beneath that, you'll see the Remoting section.  This is straight out of Remoting 101.  All I'm doing here is defining my WellKnown types.  As you see,  I am referencing the Interfaces but the actual object is going to be hosted in IIS.  This isn't the focus of the article so I'm not going to get into the nuances of Remoting right now.  Suffice to say that in the local assembly, in order to use my actual object, all I need is an interface that the object implements - no reference to the actual object is necessary other than through the configuration files.

Now, I'm going to define the QueueInfo class referenced above:

using System;

namespace KDN.Messaging.Global
{

  public class QueueInfo
   {
      
private System.String mDescription;
      
private System.String mName;
      
/// <summary>
     /// Default Constructor
     ///
     public QueueInfo(){}

      

     public System.String Description{
        
get{return mDescription;}
        
set{mDescription = value;}
      }
  

     public System.String Name{
        
get{return mName;}
        
set{mName = value;}
      }
   }
}

Hopefully there's nothing too hard to figure out about this class.  Next I'm going to create a Strongly Typed Collection to hold these objects:

using System;
using System.Collections;
using System.Xml;
using System.Configuration;

namespace SCBOS.Core.Messaging.Global
{
  
/// <summary>
  /// Summary description for QueueCollection.
  ///
  public class QueueInfoCollection : CollectionBase, System.Configuration.IConfigurationSectionHandler
   {
      
/// <summary>
     /// Default contructor - nothing needs to be done
     /// because the List property is already initialized
     /// by the base class.
     ///
       static QueueInfoCollection(){}


      
/// <summary>
     /// Overridden from CollectionBase - Adds a QueueInfo
     /// object to the collection
     ///
     /// QueueInfo
     /// <returns>System.Int32
     public System.Int32 Add(QueueInfo value){
        
return this.List.Add(value);
      }
      
/// <summary>
     /// Overridden form CollectionBase - removes a Queue
     ///   Info object from the List Collection
     ///
     /// QueueInfo
     public void Remove(QueueInfo value){
        
this.List.Remove(value);
      }
      
/// <summary>
     /// Overridden from CollectionBase - serves as the
     /// default property/indexer for the collection
     ///
     public QueueInfo this [System.Int32 index]{
        
get{return this.List[index] as QueueInfo;}
        
set{this.List[index] = value;}
      }

      
/// <summary>
     /// Overridden from CollectionBase - returns
     /// true or false depending on whether or not the
     /// specified queue exists in the collection or not
     ///
     /// QueueInfo
     /// <returns>bool
     public bool Contains(QueueInfo value){
        
return this.List.Contains(value);
      }
      
      
/// <summary>
     /// Overridden from CollectionBase - Inserts a
     /// given QueueInfo object into the collection at the
     /// position specified by the index (which is 0 based).
     ///
     /// System.Int32
     /// QueueInfo
     public void Insert(System.Int32 index, QueueInfo value){
        
this.List.Insert(index, value);
      }
      
/// <summary>
     ///
     ///
     ///
     ///
     ///
     /// <returns>
     public object Create(object parent,
                    
object configContext,
                      XmlNode section){
        
foreach(XmlNode node in section.ChildNodes){
          QueueInfo queInfo =
new QueueInfo();
          queInfo.Agency  = node.Attributes["Description"].Value;
          queInfo.Name    = node.Attributes["queueName"].Value;  
        
this.Add(queInfo);

         }
        
return this;
      }
   }
}


Now, the two main things to note here are that 1)  I'm inheriting from CollectionBase and 2) I'm implementing  
the IConfigurationSectionHandler interface.  Now, as soon as the application starts and I create a class of QueueInfoCollection - it will look to the configuration file and create a collection of QueueInfo objects for me to host in that collection.  Note that I don't have to go through the config file, parse XML, or anything else, this happens for me automatically by virtue of the fact that I have everything defined as I do.  

Now as far as the remoting goes, again, by virtue of the fact that I have everything defined in the configuration file, whatever business object I may have out there to handle moving data to and from the queue - which may be on my local machine or may not, I can handle everything from here- change the port, the application server, just about anything just from the config file.  So if I wanted to host my business objects on another machine, all I need to do is change the localhost reference (and ostensibly the port number).  BUT MOST IMPORTANTLY, I can identify and configure my queues locally.  So even if the entire network went down, as long as I was sending my messages to a queue that was available locally, my app still runs.  I don't need to look elsewhere for any configuration information and although my app wouldn't be of much value in an enterprise scenario if it couldn't access the network or database, it could in fact run and then process the info in the queues as soon as things got back up.  To realize the value of this, think of your network printer.  Let's say someone in Finance just printed out an 900 page report and you tried printing one page that you really needed ASAP.  In the traditional client/server approach to software design, you'd get database timeouts in all likelihood until the job was finished - you'd have to manually poll each time and at some point, you can everyone else would eventually get your page printed.  But wouldn't it be a lot nicer to just hit print, walk over to the printer, see that there are 10 jobs ahead of yours, head to lunch, and know that when you came back you're stuff would be ready?  As opposed to sitting there waiting for everyone else to finish?  The example I used showed a little more complext scenario than the first article, but I still only used two values.  However there are other collection objects that would have gotten me to the same place.  I could have used a Name/Value pair for instance.  But I also could have had a collection of 30 property objects (although in that case, it would get a little ugly).  So in the first article, if you have one block of settings with many properties, than you're probably better off using the approach I showed there.  If you have multiple objects though, that simply won't work.  In those cases, an approach like this one would serve you much better.  

As an aside, I really can't underestimate the value of implementing Service Oriented Architecture in an Enterprise scenario.  Life is just soooo much easier.  Changes can be made painlessly.  Users aren't burdened with all of the headaches of jumping through the hoops of you're quick fixes - God knows I've been guilty of this in the past.  All in all, life is easier for everyone.  And whenever you finally have Remoting mastered- you can move on to Web Service Enhancements (ideally 2.0) if you really want to earn some cool points.