Custom Configuration Pattern 101

So if you’ve even needed to store custom data in your App.config or Web.config files before – you’ve probably used the ConfigurationManager.AppSettings for name/value pair data – but what if you have more complex settings that you want to store in the config files?

Such as…
 

<Certificate findValue="7776F8E373CF19EEEC88293031893B8D985792F8" 
             storeLocation="LocalMachine" 
             storeName="TrustedPeople" 
             x509FindType="FindByThumbprint"/>

What do we do?  We extend .NET’s System.Configuration.ConfigurationSection of course… 🙂

First, let’s create the ever popular console test application.

Open Visual Studio –> “File” –> “New” –> “Project”.  Choose “Console Application” and name it “CustomConfigurationPattern”.

Right click on the “Solution” and click “Add” –> “New Project” –> choose “Class Library” and name it “ConfigurationLoader”.

imageNow add the “System.Configuration” reference to both projects.

 

 

 

Now in the “ConfigurationLoader” project –> “Add” –> ‘New Class…” and name it “CertificateConfigurationSingle” – the code is as follows:
 

   1:  using System.Configuration;
   2:   
   3:  namespace ConfigurationLoader
   4:  {
   5:      /// <summary>
   6:      /// Used for loading values from the following configuration xml
   7:      ///  <certificate 
   8:      ///    findValue="7776F8E373CF19EEEC88293031893B8D985792F8" 
   9:      ///    storeLocation="LocalMachine" 
  10:      ///    storeName="TrustedPeople" 
  11:      ///    x509FindType="FindByThumbprint" />
  12:      /// </summary>
  13:      public class CertificateConfigurationSingle : ConfigurationSection
  14:      {
  15:          #region Fields
  16:   
  17:          private const string _sectionNameXml = "Certificate";
  18:          private const string _findValueXml = "findValue";
  19:          private const string _storeLocationXml = "storeLocation";
  20:          private const string _storeNameXml = "storeName";
  21:          private const string _x509FindTypeXml = "x509FindType";
  22:   
  23:          #endregion
  24:   
  25:          #region Properties
  26:   
  27:          /// <summary>
  28:          /// Gets the settings.
  29:          /// </summary>
  30:          /// <value>
  31:          /// The settings.
  32:          /// </value>
  33:          public static CertificateConfigurationSingle Settings
  34:          {
  35:              get
  36:              {
  37:                  return (CertificateConfigurationSingle)
  38:                      ConfigurationManager.GetSection(_sectionNameXml);
  39:              }
  40:          }
  41:   
  42:          /// <summary>
  43:          /// Gets or sets the find value.
  44:          /// </summary>
  45:          [ConfigurationProperty(_findValueXml)]
  46:          public string FindValue
  47:          {
  48:              get { return (string)this[_findValueXml]; }
  49:              set { this[_findValueXml] = value; }
  50:          }
  51:   
  52:          /// <summary>
  53:          /// Gets or sets the store location.
  54:          /// </summary>
  55:          [ConfigurationProperty(_storeLocationXml)]
  56:          public string StoreLocation
  57:          {
  58:              get { return (string)this[_storeLocationXml]; }
  59:              set { this[_storeLocationXml] = value; }
  60:          }
  61:   
  62:          /// <summary>
  63:          /// Gets or sets the name of the store.
  64:          /// </summary>
  65:          [ConfigurationProperty(_storeNameXml)]
  66:          public string StoreName
  67:          {
  68:              get { return (string)this[_storeNameXml]; }
  69:              set { this[_storeNameXml] = value; }
  70:          }
  71:   
  72:          /// <summary>
  73:          /// Gets or sets the type of the X509 find.
  74:          /// </summary>
  75:          [ConfigurationProperty(_x509FindTypeXml)]
  76:          public string X509FindType
  77:          {
  78:              get { return (string)this[_x509FindTypeXml]; }
  79:              set { this[_x509FindTypeXml] = value; }
  80:          }
  81:   
  82:          #endregion
  83:      }
  84:  }

Next – we need to add a little more to our App.config – we need to specifically add the <configSections> xml – in which we need to have the “name” match the root xml element, in this case it’s our “<Certificate …” tag.  Then we need the “type” to match our fully qualified class name, followed by the assembly name.  Our full App.config should look like this…
 

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:    <configSections>
   4:      <section name="Certificate" type="ConfigurationLoader.CertificateConfigurationSingle, ConfigurationLoader"/>
   5:    </configSections>
   6:   
   7:  <Certificate findValue="7776F8E373CF19EEEC88293031893B8D985792F8" 
   8:                storeLocation="LocalMachine" 
   9:                storeName="TrustedPeople" 
  10:                x509FindType="FindByThumbprint"/>
  11:   
  12:  </configuration>

 

So looking at the code from our “CertificateConfigurationSingle” class – we first see that our class is derived from the System.Configuration.ConfigurationSection.
 

public class CertificateConfigurationSingle : ConfigurationSection

Next, starting on line 33 the code uses the ConfigurationManager.GetSection() method with the “SectionName” variable matching the “Certificate” tag in the App.config file’s <configSections>.  If something is incorrect in your App.config – this is where an exception will be thrown.
 

public static CertificateConfigurationSingle Settings
{
    get
    {
        return (CertificateConfigurationSingle)
            ConfigurationManager.GetSection(_sectionNameXml);
    }
}

Then all that is left to do is to expose the matching properties, such as for “FindValue” – in the following format.  Note, the [ConfigurationProperty()] attribute needs to have the corresponding xml tag associated ( I pass them in as a “private const string”s as I hate “magic” strings – and having it once as a “const string” will prevent typos )
  

/// <summary>
/// Gets or sets the find value.
/// </summary>
[ConfigurationProperty(_findValueXml)]
public string FindValue
{
    get { return (string)this[_findValueXml]; }
    set { this[_findValueXml] = value; }
}

Now that we have that done – all we need is to call it from our console app as follows…
 

   1:  using System;
   2:  using ConfigurationLoader;
   3:   
   4:  namespace CustomConfigurationPattern
   5:  {
   6:      class Program
   7:      {
   8:          static void Main()
   9:          {
  10:              //----------------------------------------------------------------------
  11:              // Load a single element certificate
  12:              //----------------------------------------------------------------------
  13:              Console.WriteLine(CertificateConfigurationSingle.Settings.FindValue);
  14:              Console.WriteLine(CertificateConfigurationSingle.Settings.StoreLocation);
  15:              Console.WriteLine(CertificateConfigurationSingle.Settings.StoreName);
  16:              Console.WriteLine(CertificateConfigurationSingle.Settings.X509FindType);
  17:              Console.WriteLine("----------------------");
  18:   
  19:              Console.ReadLine();
  20:          }
  21:      }
  22:  }

Now when we run it – we get awesome sauce!
 
image

In “Custom Configuration Pattern 102” – we will take this further and look at parsing even more complex nested custom configuration items such as…
 

<CertificateSection authenticationMode="Test">
  <Certificates>
    <add certificateType="cipherCertificate" 
          findValue="E86D56AC84D51F9C2D12C898341F5FA38E909B02" 
          storeLocation="LocalMachine" 
          storeName="TrustedPeople" 
          x509FindType="FindByThumbprint"/>
    <add certificateType="serviceCertificate" 
          findValue="3D25DB176A2098F010C288761F5B367F3DFA07FA" 
          storeLocation="LocalMachine" 
          storeName="TrustedPeople" 
          x509FindType="FindByThumbprint"/>
  </Certificates>
</CertificateSection>

Good coding!

…Lance

Leave a Reply