Click here to Skip to main content
15,896,557 members
Articles / Web Development / ASP.NET

Configuration Manager for the Enterprise

Rate me:
Please Sign up or sign in to vote.
4.58/5 (27 votes)
8 Aug 2007CPOL20 min read 63.1K   341   73  
SysConfiguration is a component that allows the storing of configuration data for an enterprise application. It is intended to be an improved version of the System.Configuration class or the Enterprise Library (Microsoft Patterns and Practices).
<!--------------------------------------------------------------------------->  
<!--                           INTRODUCTION                                

 The Code Project article submission template (HTML version)

Using this template will help us post your article sooner. To use, just 
follow the 3 easy steps below:
 
     1. Fill in the article description details
     2. Add links to your images and downloads
     3. Include the main article text

That's all there is to it! All formatting will be done by our submission
scripts and style sheets. 

-->  
<!--------------------------------------------------------------------------->  
<!--                        IGNORE THIS SECTION                            -->
<html>
<head>
<title>The Code Project</title>
<Style>
BODY, P, TD { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt }
H2,H3,H4,H5 { color: #ff9900; font-weight: bold; }
H2 { font-size: 13pt; }
H3 { font-size: 12pt; }
H4 { font-size: 10pt; color: black; }
PRE { BACKGROUND-COLOR: #FBEDBB; FONT-FAMILY: "Courier New", Courier, mono; WHITE-SPACE: pre; }
CODE { COLOR: #990000; FONT-FAMILY: "Courier New", Courier, mono; }
</style>
<link rel="stylesheet" type=text/css href="http://www.codeproject.com/styles/global.css">
</head>
<body bgcolor="#FFFFFF" color=#000000>
<!--------------------------------------------------------------------------->  


<!-------------------------------     STEP 1      --------------------------->
<!--  Fill in the details (CodeProject will reformat this section for you) -->

<pre>Title:       Configuration Manager for the Enterprise
Author:      Randar Puust 
Email:       Developer@Puust.com
Member ID:   1690860
Language:    C# 2.0, XML, XSD
Platform:    Windows, .NET 2.0
Technology:  ASP.NET, COM+, Web Services, NUnit, NUnitASP, XSDObjectGen, Test Driven Development
Level:       Intermediate
Description: SysConfiguration is a component that allows the storing of configuration data, for an 
enterprise application.  It is intended to be an improved version of the System.Configuration class 
or the Enterprise Library (Microsoft Patterns and Practices).
Section      C# Programming
SubSection   Applications
</pre>

<!-------------------------------     STEP 2      --------------------------->
<!--  Include download and sample image information.                       --> 

<ul class=download>
<li><a href="SysConfiguration.zip">Download solution - 1006 Kb </a></li>
</ul>

<p><img src="Images\ClassDiagram.jpg" alt="SysConfiguration"/></p>


<!-------------------------------     STEP 3      --------------------------->
<!--  Add the article text. Please use simple formatting (<h3>, <p> etc)   --> 
<h2>Introduction</h2>

<p>SysConfiguration is a component that allows the storing of
configuration data, for an enterprise application.&nbsp; It is intended to be an
improved version of the System.Configuration class or the Enterprise Library
(Microsoft Patterns and Practices).</p>

<h3>Enterprise Configuration</h3>

<p>I work for a group at <a href="http://www.t4g.com/">T4G Limited</a>, which creates complex custom applications
for large enterprises.&nbsp;
    It�s not uncommon that a single application has
multiple user interfaces (consumer, call center, B2B, etc.).&nbsp; As a result, we
often have multiple architectures (web, web services, WinForm, COM+, etc.)
running in parallel.&nbsp; As a technology services company, there is a high degree
of requirement volatility, which means we have frequent deployments to multiple
environments (Development, Test, Staging, User Acceptance Testing, Production,
etc.).&nbsp; In line with our companies atmosphere, the goals of this component were:</p>

<ul style='margin-top:0cm' type=disc>
 <li>Automatically detect if a configuration setting was
     missing.&nbsp;
 <ul style='margin-top:0cm' type=circle>
  <li><strong>Problem: </strong>A big frustration with the App.Config is that
      it�s loosely typed.&nbsp; This means that if there is setting that is used by
      a component, you have to actually test that component before you can
      determine whether it�s there or not.&nbsp; </li>
  <li><strong>Solution:</strong> SysConfiguration uses a strongly typed class.&nbsp;
      The very first time it�s loaded, it will validate the configuration data
      against a schema to ensure that all mandatory settings are there, while
      still allowing for optional settings.&nbsp; This is a huge benefit when moving
      code through the environments (e.g. Development to Testing to Production)</li>
 </ul>
 </li>
 <li>Works for multiple architectures
 <ul style='margin-top:0cm' type=circle>
  <li><strong>Problem:</strong>  If you have an application that has multiple
      architectures, each one stores their configuration in different place:
      Web.config for web applications and web services, App.config for WinForm
      and console applications, registry for COM+, etc.&nbsp; The Enterprise Library
      does have the FileConfigurationSource class, which allows you to call a
      centralized configuration file, which is an improvement over the default
      .Net Framework.</li>
  <li><strong>Solution:</strong> SysConfiguration can use the same file for all
      architectures.&nbsp; The only difference is the location of two entries that
      point to the location of the configuration file and the schema that
      validates it.&nbsp; This provides a degree of flexibility, because systems can
      share the same configuration, or point to different locations if you want
      to keep them distinct.</li>
 </ul>
 </li>
 <li>Strongly Typed Hierarchical Data
 <ul style='margin-top:0cm' type=circle>
  <li><strong>Problem:</strong> Although you can create hierarchies with
      System.Configuration, when you actually access the property, you still
      have to use <em>AppSettings[�LooselyCoupledKey�]</em>.
      This does not allow typos
      to be detected at compile time, only at runtime.</li>
  <li><strong>Solution:</strong> SysConfiguration generates a hierarchical
      object model that represents the configuration class.&nbsp; This means you get
      code that is easy to understand, caught at compile time and can use
      IntelliSense (e.g. <em>SysConfiguration.Data.AppConfig.ConnectionStrings.Core</em>).
  </li>
 </ul>
 </li>
 <li>Standardized configuration entries
</li>
 <ul style='margin-top:0cm' type=circle>
  <li><strong>Problem:</strong> At T4G, we tend to share components whenever we
      can.&nbsp; If you want to pass a component to another person, it would be
      desirable to reliably pass the configuration entries specific to that
      component.&nbsp; If you use something like App.Config, all you can do is hope
      that somebody has commented it well enough to indicate which settings
      belong to which components.</li>
  <li><strong>Solution:</strong> SysConfiguration is designed in a way that the
      hierarchical structure of the configuration can be broken up into many
      files.&nbsp; For example, they could be broken up into: AppConfig, which
      stores settings that are specific to the current application, T4GToolbox,
      which stores all of the settings for our re-usable framework and
      RightsConfig which stores the settings for a re-usable security component,
      etc.&nbsp;
      SysConfiguration is designed to merge all of these definitions into
      a single configuration file, so there is only one file to maintain per
      environment.</li>
 </ul>
</ul>

<p>
    <img id="IMG1" src="Images\Deployment.GIF" alt="SysConfiguration Deployment Diagram" />&nbsp;</p>

<h3>Flexible Singleton</h3>

<p>An ideal design pattern for storing configuration data is
the <a href="http://www.dofactory.com/Patterns/PatternSingleton.aspx">Singleton</a>.&nbsp;
You want the configuration data to be loaded from disk once and then kept
somewhere fast so that it can be accessed quickly.&nbsp; The problem with the
Singleton, is that almost all the examples just keep it in memory.&nbsp; If this was
used in a web context, it would re-load the data for each request.&nbsp; If it was
used in COM+ component, it would release that memory as soon as it was idle for
a period of time.</p>

<p>SysConfiguration detects the context it is running as, and
then stores it in an appropriate storage:</p>

<ul>
 <li>For web applications and web services, it uses the Web
     Cache.</li>
 <li>For WinForm and Console applications, it uses memory.</li>
 <li>For COM+, it uses the Shared Property Manager.</li>
</ul>

<p>
    <img src="Images\Data Persistence.GIF" alt="Data Persistence" />&nbsp;</p>

<h3>Test Driven Development</h3>

<p>I also wanted to mention that this project was entirely
built using Test Driven Development (TDD), although I probably didn�t follow
the principles of &quot;You Ain't Gonna Need It&quot; (<a href = "http://c2.com/xp/YouArentGonnaNeedIt.html">YAGNI</a>) as closely as I
could have, I feel it was the correct balance of optimization and future use.&nbsp;
Although I�ve used TDD for components before, I used this project as a test bed
for some of the claims made by proponents of TDD and see for myself whether
they are true.&nbsp; See <i>Conclusions about Test Driven Development</i>.</p>

<p>In terms of the process I followed, I consistently wrote the
test first, failed it (red), got the code to work according to the test
(green), refactored the unit test and started the cycle all over again.&nbsp;
</p>

<p>I�ve included all the unit tests, which may be of interest
because I feel I built out a fairly good process when testing a single
component across all the architectures.&nbsp;
    The most important design decision is
that there are a set of tests, called by NUnit, but in many cases those tests
call other tests residing in different processes.&nbsp;
    So there is a central
assembly that NUnit calls, but the actual unit testing logic is stored
somewhere else.&nbsp; This ensures I can call all of the unit tests at once any time
I need to.</p>

<p>
    <img src="Images\Unit Testing.GIF" alt="Unit Testing Classes" />&nbsp;</p>

<p></p> 
<h2>Using the Code</h2>

<p>
    The install includes optional steps depending on your goals. The first set of steps are
for those people who are interested in the configuration component.&nbsp; The second
set of steps are additional steps, if you want to learn more about Test Driven
Development.</p>

<p>I should mention, there are some manual steps for setting
this up, but once it has been integrated, it is very easy to maintain.</p>

<h3>Pre-Requisites for SysConfiguration</h3>

<ol>
 <li>The project was built using the .Net Framework 2.0 and Visual
     Studio 2005. &nbsp;&nbsp;
 </li>
 <li>You will need to install XSDObjectGen (<a href ="http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&amp;displaylang=en">http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&displaylang=en</a>).&nbsp;
     This is used to generate the configuration class generated by the schema.&nbsp;
     It assumes the default install path is used, which will put the VS2005
     version into C:\Program Files\XSDObjectGenerator\vs2005.</li>
</ol>

<h3>Additional Steps to Use Unit Tests (optional)</h3>

<p>If you are interested in looking at executing the Unit
Tests, there are a few more additional steps.</p>

<ol>
 <li>Install the latest version of NUnit.&nbsp; At the time of this
     article, it was up to version 2.4 (<a href="http://www.nunit.org/">http://www.nunit.org/</a>).</li>
 <li>Download NUnitASP (<a
     href="http://nunitasp.sourceforge.net/">http://nunitasp.sourceforge.net/</a>).&nbsp;
     At the time of this article, it was version 1.5.1.&nbsp; Extract the zip file
     to C:\Program Files\NUnitASP.</li>
 <li>Install NUnit 2.2 from the bin directory of NUnitASP.&nbsp; You
     will be able to run both NUnits in parallel.</li>
 <li>I had to add &lt;supportedRuntime version=&quot;v2.0.50727&quot;
     /&gt; to the nunit.exe.config and nunit-console.exe.config.&nbsp; You may have
     to do the same depending on the .Net runtimes installed on your machine. </li>
 <li>You will need to be on an operating system that supports
     Component Services or COM+.&nbsp;
     There is a class with tests that are
     installed using the regsvcs.exe command line tool.</li>
 <li>There are some constants in UnitTestCommon\Constants.cs
     that will have to be changed.&nbsp;
     These are set to paths that it expects back,
     which may differ from your file system.&nbsp;
     You should be able to figure
     these out the first time you run the unit tests and it fails.</li>
     <li>Open the properties for the UnitTests project.&nbsp; Go to Debug.&nbsp; Choose the Start Action to be "Start External Program".  Select the latest version of nunit.exe.</li>      
     <li>Open up the properties for the WebTestHarness project.&nbsp; Go to Web.  Select "Don't open a page.  Wait for a request from an external application."</li>      
     <li>Open the solution properties.&nbsp; Go to Startup Project, select "Multiple startup projects" and choose "UnitTests" and "WebTestHarness" to both start.</li>      
</ol>

<h3>Adding SysConfiguration</h3>

<ol>
 <li>The project is not designed as a standalone component.&nbsp;
     There is a class that is automatically generated based on the schema file,
     each time the project is built.&nbsp;
     This file is then compiled, along with
     the rest of the project.&nbsp; Therefore, it needs to be added to the solution
     of your application.
    
 <ol style='margin-top:0cm' start=1 type=a>
  <li>If you don�t care about unit tests, copy the
      Configuration project into your solution.</li>
  <li>If you want the unit tests, copy the Configuration
      project to your main solution.&nbsp;
      Copy all of the other projects to
      wherever you are keeping your unit tests.&nbsp;
      In my case, I kept them in a
      folder in my Solution so there is a clear separation.&nbsp;
      See SysConfigurationWithUnitTests.sln
      for an example.</li>
 </ol>
</li>

</ol>
    <p>
        <img src="Images\Solution.JPG" style="border-right: thin solid; border-top: thin solid; border-left: thin solid; border-bottom: thin solid" alt="Screenshot of Solution" />&nbsp;</p>

 <ol style='margin-top:0cm' start=2 type=1>
  <li>You will have to configure your application to find the configuration file and the
      schemas that validate it.
       <ol style='margin-top:0cm' start=1 type=a>
     <li>If it is a COM+ component, SysConfiguration needs to use
      the registry to find the paths.&nbsp; There is a sample .reg file SetupComPlus.reg
      provided.&nbsp; It will put two keys (<em>ConfigurationPath</em> and <em>ConfigurationSchemaPath</em>)
      into the <span style='font-size:10.0pt;font-family:"Courier New"'><em>HKEY_LOCAL_MACHINE\SOFTWARE\Name.Randar</em>
      </span>key. </li>
           <li>For any web based application, add the following to the
      Web.config and point the paths to the correct location.&nbsp; For a .Net
      application, make the same changes, but to the App.config. </li>
      </ol> </li>
 </ol>
<pre>&lt;?xml version="1.0" ?&gt;
&lt;configuration&gt;
	&lt;!-- Add these items to your configuration file and point them at the location that has the configuration and schema--&gt;
	&lt;appSettings&gt;
		<strong>&lt;!-- Development Configuration Path --&gt;
		&lt;add key="ConfigurationPath" value="C:\Projects\SysConfiguration\Code\Configuration\Development\Configuration.xml" /&gt;
		&lt;!-- Production Configuration Path --&gt;
		&lt;!-- &lt;add key="ConfigurationPath" value="C:\Projects\SysConfiguration\Code\Configuration\Development\Configuration.xml" /&gt; --&gt;
		&lt;!-- Path to Schema used to validate configuration files--&gt;
		&lt;add key="ConfigurationSchemaPath" value="C:\Projects\SysConfiguration\Code\Configuration\Schemas\Settings.xsd" /&gt;
</strong>	&lt;/appSettings&gt;
&lt;/configuration&gt;
</pre>
<p></p>
<ol start=3 type=1>
      <li>If there are dependent schemas, you will have to change
     the absolute paths of the <span style='font-size:10.0pt;font-family:"Courier New";
     color:maroon'>xs:include</span> in Settings.xsd.&nbsp; There does not seem to
     be way to make the path relative, which is unfortunate.&nbsp; I would recommend
     you try to keep the same file structure on disk across the various
     environments (development, staging, etc.) or make sure to not overwrite
     your Settings.xsd when you update the code. </li> 
 </ol>  
     

<h3>SysConfiguration Tasks</h3>

<p>The following is a �help file� of common tasks you would
need to perform to use SysConfiguration.</p>

<p><b>To add a configuration element</b></p>

<p>The following describes the steps to add a new configuration
setting to the application, once the pre-requisite steps have been followed.&nbsp;
SysConfiguration is designed to allow developers to quickly add new
configuration settings and allow them to define the validation required.</p>

<ol>
 <li>If you would like to add a configuration element, simply
     change the definition in one of the schemas (e.g. AppConfig.xsd,
     RightsConfig.xsd, etc.).&nbsp; Do not add configuration elements to
     Settings.xsd.&nbsp; This is a central hub of the various configuration
     definitions.It will generate a class that has all references to all the
     other configuration files.&nbsp; &nbsp; &nbsp; Simliar in concept to a project file.</li>
 <li>The following are some examples that are supported:
 <ol>
  <li>Data types.&nbsp; If you set the type of a <em>xs:element</em>, it will
      create a valid .Net type.</li>
  <li>minOccurs and maxOccurs.&nbsp; This allows you to create
      collections if the maxOccurs is unbounded, and optional settings if
      minOccurs is 0.</li>
 </ol>
</li>
</ol>

<p><b>To add configuration data</b></p>

<p>Once the structure has been defined, the following are the
steps to add the configuration data.</p>

<ol>
 <li>Open up Configuration.xml in your favourite XML editor.&nbsp;
     If you are using Visual Studio, it should have the targetNamespace setup
     so that that Intellisense will allow you to build the hierarchy and add
     the data.</li>
</ol>

<p><b>&nbsp;</b>&nbsp;<b>To access a configuration element</b></p>

<p>Once the configuration definition has been defined, and the
data added, the following describes how the application would access that data.</p>

<ol>
 <li>Add a reference to Name.Randar.Configuration.dll.</li>
 <li>Add a using statement for the namespace (i.e. 
     <em>using Name.Randar.Configuration</em>)</li>
 <li>Access the configuration hierarchy, starting with the
     Singleton called SysConfiguration.&nbsp;
     You can access the data either through &nbsp;the static Data or Instance.SettingsData.&nbsp;
     Both are identical.&nbsp; Some
     examples:
 <ol>
  <li>SysConfiguration.Data.AppConfig.ConnectionStrings.Core</li>
  <li>SysConfiguration.Instance.SettingsData.AppConfig.ConnectionStrings.Core</li>
  <li>SysConfiguration.Data.DebugSettings.LogToFile</li>
  <li>SysConfiguration.Instance.SettingsData.AppConfig.NumberOfRetries</li>
  <li>SysConfiguration.Data.AppConfig.CloseOutDate</li>
  <li>SysConfiguration.Instance.SettingsData.AppConfig.MaxAllowed</li>
 </ol>
</li>
</ol>

<p><b>To access a list of configuration elements</b></p>

<p>SysConfiguration supports single name\value pairs as well as
lists.&nbsp; The following describes how to access a list of data.</p>

<ol>
 <li>It stores lists as a strongly typed sub-class of the
     ArrayList class.&nbsp; So you can access it by index or iterating through the
     list.
 <ol style='margin-top:0cm' start=1 type=a>
  <li>SysConfiguration.Data.RightsConfig.RightCollection[0].Name</li>
  <li>SysConfiguration.Data.RightsConfig.RightCollection.Count</li>
 </ol>
</li>
</ol>

<pre>// Search through the roles until you find the one we are looking
int findRole = -1;
for (int i = 0; i < settings.RightsConfig.RightCollection.Count; i++ )
{
    if (settings.RightsConfig.RightCollection[i].Name == Constants.RoleToSearchFor)
        findRole = i;
}

</pre>


<p><b>To add a configuration definition</b></p>

<p>If you would like to extend the configuration definition,
but leave the main definition alone, you can use xs:include to include other
schemas.&nbsp;
    This allows you to break up the configuration definition based on
components or other requirements.</p>

<ol>
 <li>Create your schema that defines your configuration.&nbsp; Make
     sure it has a root element.</li>
 <li>Modify <em>Settings.xsd</em>:
 <ol style='margin-top:0cm' start=1 type=a>
  <li>Add a <em>xs:include</em> and point it at the new file.</li>
  <li>Create an element that references the base element of the
      other schema file.</li>
 </ol>
</li>
</ol>

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;xs:schema id="Settings" targetNamespace="http://Randar.Name/Settings.xsd" elementFormDefault="qualified" 
xmlns="http://Randar.Name/Settings.xsd" xmlns:mstns="http://Randar.Name/Settings.xsd" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
	&lt;xs:include schemaLocation="C:\Projects\SysConfiguration\Code\Configuration\Schemas\AppConfig.xsd"&gt;
	&lt;/xs:include&gt;
	&lt;xs:include schemaLocation="C:\Projects\SysConfiguration\Code\Configuration\Schemas\RightsConfig.xsd"&gt;
	&lt;/xs:include&gt;
	&lt;xs:element name="Settings"&gt;
		&lt;xs:complexType&gt;
			&lt;xs:sequence&gt;
				&lt;xs:element ref="AppConfig"&gt;
				&lt;/xs:element&gt;
				&lt;xs:element ref="RightsConfig"&gt;
				&lt;/xs:element&gt;
				&lt;!-- Add new elements here that reference other schema files--&gt;
			&lt;/xs:sequence&gt;
		&lt;/xs:complexType&gt;
	&lt;/xs:element&gt;
&lt;/xs:schema&gt;

</pre>


<p><b>To reload the configuration data</b></p>

<p>You should setup your application so there is a way to clear
the cache and force it to re-load the data next time it is used.&nbsp; Otherwise,
you will have to restart the process somehow to clear the data.&nbsp; The following
steps describe how this can be done:</p>

<ol>
 <li>You will have to create something that can be called
     manually.&nbsp; For example:
 <ol style='margin-top:0cm' start=1 type=a>
  <li>A web page in an admin area with a button called �Clear
      Cache�.</li>
  <li>A web service with a web method called �ClearCache�.</li>
  <li>A button on the Help-&gt;About screen that has a button
      �Reload Configuration�.</li>
 </ol>
 </li>
 <li>In all of these cases, the method should call <em>SysConfiguration.ClearCache()</em>.
     </li>
</ol>

    <h2>
        Out of Scope</h2>
    <p>
        The following scenarios have not been tested or are not fully supported</p>
    <ul>
        <li><strong>Backwards compatibility.&nbsp; </strong>I have purged my development machine
            of all older development environments, including VB6.&nbsp; Therefore, I haven�t
            tested it on anything older than the .Net 2.0 Framework.</li>
        <li>
            <ul type=circle>
                <li>I was able to register to the component using Regasm.exe, view it in Ole Viewer,
                    but none of the classes had methods.&nbsp; I haven�t needed to do COM\.Net Integration
                    since 2002, so I�m sure there is something small I have forgotten, but didn�t feel
                    this was a priority.</li>
                <li>If you need to port it to the 1.0 framework:
                <ul>
                    <li>You will have to use an older solution file, or manually edit the solution using
                        a text editor.</li>
                    <li>Collapse the SysConfiguration into a single class instead of using a Partial class.</li>
                    <li>Use the legacy classes instead of 2.0 System.Configuration Namespace.</li>
                </ul>
            </li>
            </ul>
        </li>
        <li><b>Inheritance.&nbsp; </b>Unfortunately, it is hard to make to make the Singleton
            design pattern inheritable, without some sort of hack, such as the base class knowing
            of the super class.&nbsp; It is unfortunate, because I can think of many scenarios
            where SysConfiguration could be a good base class.&nbsp; My best compromise to this
            was to break up the Singleton and configuration functionality using Partial classes.</li>
        <li><b>C# only.&nbsp; </b>It was enough work building this once.&nbsp; I didn�t have
            time to create it in VB.Net as well.</li>
        <li><b>Centralized configuration data. </b>Some third party configuration managers have
            a central location for configuration data, that all local and remote processes access.&nbsp;
            I�ve never liked this architecture because you have so many cross process\network
            calls.&nbsp; If you really wanted this, you could load it into Component Services
            and make all the calls through there, but you will get a performance hit.</li>
        <li><b>Configuration structural changes require recompilation.&nbsp; </b>As I mentioned
            before, you cannot change the structure of the configuration, without recompiling.&nbsp;
            This is because of the strongly typed nature of this project.&nbsp; I don�t feel
            this is a deficiency because you would have to change your code to access the new
            configuration element anyways, but wanted to bring it up.&nbsp; Obviously, you can
            change the configuration data without recompilation, just not the structure.</li>
        <li><b>Editing configuration.</b>&nbsp; There is no built in support for editing the
            configuration data.&nbsp; There are probably reasons for doing this, but I�ve never
            run across it.&nbsp; If the data is volatile, I will always store it in the database
            so it can be shared across the server farm.&nbsp; At the end of the day, it�s just
            XML, so you could edit it using the standard XML classes.</li>
        <li><b>English only.&nbsp; </b>I didn�t make it multi-lingual and I probably should
            have.&nbsp; Although the only information that is shown is error messages, so I
            got a bit lazy.</li>
        <li><b>Not CLS compliant.</b>&nbsp; Although my code is CLS compliant, the code generated
            by XSDObjectGen is not.&nbsp; The generated code is a key item, that I felt it best
            to mark the entire class as non CSL compliant.</li>
        <li><b>Automatic file change detection.&nbsp; </b>&nbsp; A nice (or not nice) feature
            of App.config and Web.config is that it will restart the process if the config file
            changes, reloading the settings.&nbsp; I have not looked into replicating this feature
            (or defect, depending on your point of view).&nbsp; I personally think I prefer
            being able to force the reload manually, but would like to make this an optional
            feature.</li>
    </ul>
    <h2>Points of Interest</h2>

<p>The following are some comments about the code and
development process that may be of interest to some people.</p>

<h3>Conclusions about Test Driven Development</h3>

<p>The following section describes my opinion of Test Driven
Development when compared to the traditional process of developing the code and
(maybe) creating automated unit tests.&nbsp; It not based on any empirical study and
should be treated as opinion, not fact.</p>

<p>I definitely feel that TDD helped me make a much more robust
component than using traditional development.&nbsp; It was very good for at testing
the various architectures quickly and repeatedly.&nbsp; A few times, I made some
major refactoring of the architecture and having a full regression test was
invaluable in helping me to get the component functional again.</p>

<p>In terms of whether I actually developed this faster using
TDD than traditional development methodologies, I am not sure I agree.&nbsp;
Creating and refactoring the unit tests does take time and can�t be discounted.&nbsp;
The argument is that creating the tests, takes less time than debugging the
problem.&nbsp; The only way to accurately test this would be to clone a person and
have them develop the identical component, under identical conditions but using
both techniques, which is just not possible.&nbsp; I do think you would see a much
greater return when you have a team larger than a single person, because the
scenario of another developer breaking somebody else�s code is much more
likely.</p>

<p>Some proponents of TDD will say that when tests fail
unexpectedly, reverting the code to the last version that passed all tests is
almost always more productive than debugging.&nbsp; I have to say, I did spend
considerably less time debugging than usual, but don�t feel I should have ever
rolled back.&nbsp; I think debugging was always the better option, especially
considering how good the debugger is in VS2005.</p>

<p>Even though this version was developed by a team of one, I
tried to act like I was working in a team.&nbsp; I tried to make sure to only check
in the test, when the underlying code passed (i.e. green).&nbsp; In theory, this
means that if we were using Continuous Integration, it would always pass.&nbsp; The
only issue was to fight the urge to check in the test once it was written.&nbsp; I�m
used to the mentality of keeping my changes small and check-ins frequent, but
you have to change your mentality slightly.</p>
     <p>
         One item of interest is the ratio of lines of code of the
unit tests vs. application code.&nbsp;
     </p>

<ul style='margin-top:0cm' type=disc>
 <li>4887 total lines of code</li>
 <li>3401 lines of unit tests</li>
 <li>1486 lines of application code</li>
</ul>

<p>So we end up with an approximate ratio of 2:1 of unit tests
to application code.&nbsp;
    That is higher than typical, although many of the tests
are duplicates for different architectures which would explain the high ratio.</p>

<p>To answer the question, of whether I would suggest using TDD
on a project, the answer is yes, with caveats.&nbsp; Although you can�t tell from
this project, I would suggest using TDD for any component that is called by
other components.&nbsp; Writing effective unit tests for the web layer is still
difficult unless you build in some way to bypass authentication and session
data.&nbsp; I definitely feel it creates much more reliable code and makes changes
easier due to the extensive regression test it creates.</p>

<h3>Quality of code</h3>

<p>Since this is intended to be used and extended by a broad
range of people, I tried to:</p>

<ul style='margin-top:0cm' type=disc>
 <li>Clearly comment the code as much as possible.</li>
 <li>Conform to the C# Coding Standards for .NET by Lance Hunt
     (<a
     href="http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx">http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx</a>),
     which is the standard used at T4G.</li>
 <li>Use FxCop to clean up some exception handling and other
     style issues.&nbsp; 
     Many of the exclusions were from the code
     XSDObjectGen generated or simply didn�t apply to what I was trying to
     accomplish (e.g. globalization).</li>
 <li>I really wanted to use <a href="http://ncover.org/site/">NCover</a> to ensure I had 100% code
     coverage with my unit tests.&nbsp;
     This was actually harder than I expected.&nbsp; Many
     of the tests are running in different processes (console, web, COM+,
     etc.).&nbsp; NCover can�t figure out they are all running from the same set of
     tests, so it showed poor coverage, even though the superset of all the
     tests would probably be close to 100%.</li>
</ul>

<h3>XSDObjectGen.exe</h3>

<p>I wanted to make a few comments about the XSDObjectGen, aka
Sample Code Generator (<a href="http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&displaylang=en">http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&amp;displaylang=en</a>).&nbsp;
</p>

<ul style='margin-top:0cm' type=disc>
 <li>At one point, instead of using XSDObjectGen.exe, I did try
     to use XSD.exe to generate my class files, but it suffers from one major
     deficiency.&nbsp; No support for the &lt;xs:import&gt; tag.&nbsp; This forces you to
     keep all of your definitions in one file, which is difficult to manage.</li>
 <li>I find XSDObjectGen so much better than Strongly Typed
     Datasets, because they are lighter and you can create deeper hierarchies
     than row and column.&nbsp; The only issue is that you have to load the data
     yourself.</li>
 <li>If you ever need to create Data Transfer Objects (<a
     href="http://en.wikipedia.org/wiki/Data_Transfer_Object">http://en.wikipedia.org/wiki/Data_Transfer_Object</a>),
     you should seriously look at XSDObjectGen.&nbsp; It creates great lightweight
     objects that serialize well.</li>
</ul>

<h2>Troubleshooting</h2>

<p><b>Problem: System is raising a </b><b><span style='font-size:8.5pt;
font-family:"MS Shell Dlg"'>System.IO.FileNotFoundException</span>.</b></p>

<p>It cannot find the configuration file or the schema used to
validate it.&nbsp; See step 2 of "Adding SysConfiguration".</p>

<p><b>Problem: The compiler is raising: Could not find a part of the
path 'C:\Projects\SysConfiguration\Code\Configuration\Schemas\RightsConfig.xsd'.
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; C:\Projects\SysConfiguration\Code\Configuration\Schemas\Settings.xsd</b></p>
    <p>
        <b></b><em>xs:import</em> seems to need the absolute path.&nbsp; Edit
<em>Settings.xsd</em> to point to the correct location.</p>

<p><b>Problem: The compiler is raising: Unable to copy file
&quot;obj\Debug (With Sample Configuration)\Name.Randar.txUnitTests.dll&quot;
to &quot;bin\DebugWSC\Name.Randar.txUnitTests.dll&quot;. The process cannot
access the file 'bin\DebugWSC\Name.Randar.txUnitTests.dll' because it is being
used by another process.</b></p>

<p>The COM+ component is still running.&nbsp; Open up Component
Services, expand COM+ Application, right click on SysConfiguration Unit Tests
and choose �Shut Down�.</p>

<h2>Conclusion</h2>

I wrote the first version of this component in 2002.&nbsp; Since
then, it�s been rebuilt and refactored many times.&nbsp; At the same time, the
overall core design has changed very little and it�s weathered the test of
time.&nbsp; This latest version and the accompanying article took quite a bit of
time to write.&nbsp; You are free to incorporate it into your code and all I ask is
that you don�t take credit for my work and to please email me with feedback
(both good and bad) if you do use it.

<!-------------------------------    That's it!   --------------------------->
     
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect OrderDynamics
Canada Canada
Shipping product and getting teams to work at peak efficiency are my passions. I’m a firm believer in Agile methodologies, proper engineering practices with a focus on Software as a Service (SaaS). My extensive knowledge of technology as well as my passion, loyalty and ability to learn quickly will add value to any company. Many of my duties have included working closely with customers and I am known for being a great communicator of ideas and concepts.

Comments and Discussions