Custom Configuration Pattern 102

So we will pick up from where we left off in “Custom Configuration Pattern 101” – we are looking to elegantly consume complex configuration settings in App.config and Web.config files – in this case we are looking to step up our game and consume the following…
  

<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>

We will continue our “CustomConfigurationPattern” console application!

imageVery similar to our “CertificateConfigurationSingle” class – create a new class called “CertificateElement” class and add the following code – noting that the only difference is the addition of the CertificateType property and added constructors and that this is derived from System.Configuration.ConfigurationElement.
 

   1:  using System.Configuration;
   2:   
   3:  namespace ConfigurationLoader
   4:  {
   5:      /// <summary>
   6:      /// Certificate element.
   7:      /// </summary>
   8:      public class CertificateElement : ConfigurationElement
   9:      {
  10:          #region Fields
  11:   
  12:          private const string _certificateTypeXml = "certificateType";
  13:          private const string _findValueXml = "findValue";
  14:          private const string _storeLocationXml = "storeLocation";
  15:          private const string _storeNameXml = "storeName";
  16:          private const string _x509FindTypeXml = "x509FindType";
  17:   
  18:          #endregion
  19:   
  20:          #region Constructors
  21:   
  22:          /// <summary>
  23:          /// Initializes a new instance of the <see cref="CertificateElement"/> class.
  24:          /// </summary>
  25:          public CertificateElement()
  26:          {
  27:          }
  28:   
  29:          /// <summary>
  30:          /// Initializes a new instance of the <see cref="CertificateElement" /> class.
  31:          /// </summary>
  32:          /// <param name="certificateType">Type of the certificate.</param>
  33:          /// <param name="findValue">The find value.</param>
  34:          /// <param name="storeLocation">The store location.</param>
  35:          /// <param name="storeName">Name of the store.</param>
  36:          /// <param name="x509FindType">Type of the X509.</param>
  37:          public CertificateElement(string certificateType, 
  38:              string findValue, string storeLocation, 
  39:              string storeName, string x509FindType)
  40:          {
  41:              this.CertificateType = certificateType;
  42:              this.FindValue = findValue;
  43:              this.StoreLocation = storeLocation;
  44:              this.StoreName = storeName;
  45:              this.X509FindType = x509FindType;
  46:          }
  47:   
  48:          #endregion
  49:   
  50:          #region Properties
  51:   
  52:          /// <summary>
  53:          /// Gets or sets the type of the certificate.
  54:          /// </summary>
  55:          [ConfigurationProperty(_certificateTypeXml)]
  56:          public string CertificateType
  57:          {
  58:              get { return (string)this[_certificateTypeXml]; }
  59:              set { this[_certificateTypeXml] = value; }
  60:          }
  61:   
  62:          /// <summary>
  63:          /// Gets or sets the find value.
  64:          /// </summary>
  65:          [ConfigurationProperty(_findValueXml)]
  66:          public string FindValue
  67:          {
  68:              get { return (string)this[_findValueXml]; }
  69:              set { this[_findValueXml] = value; }
  70:          }
  71:   
  72:          /// <summary>
  73:          /// Gets or sets the store location.
  74:          /// </summary>
  75:          [ConfigurationProperty(_storeLocationXml)]
  76:          public string StoreLocation
  77:          {
  78:              get { return (string)this[_storeLocationXml]; }
  79:              set { this[_storeLocationXml] = value; }
  80:          }
  81:   
  82:          /// <summary>
  83:          /// Gets or sets the name of the store.
  84:          /// </summary>
  85:          [ConfigurationProperty(_storeNameXml)]
  86:          public string StoreName
  87:          {
  88:              get { return (string)this[_storeNameXml]; }
  89:              set { this[_storeNameXml] = value; }
  90:          }
  91:   
  92:          /// <summary>
  93:          /// Gets or sets the type of the X509 find.
  94:          /// </summary>
  95:          [ConfigurationProperty(_x509FindTypeXml)]
  96:          public string X509FindType
  97:          {
  98:              get { return (string)this[_x509FindTypeXml]; }
  99:              set { this[_x509FindTypeXml] = value; }
 100:          }
 101:   
 102:          #endregion
 103:      }
 104:  }

Now since we’re dealing with a nested collection – we want to derive a class from the System.Configuration.ConfigurationElementCollection class.  The following code shows our two required “protected override” methods and an Add() method – the latter is used for Unit Testing and will be shown more in our upcoming “Custom Configuration Pattern 103” post.
  

   1:  using System.Configuration;
   2:   
   3:  namespace ConfigurationLoader
   4:  {
   5:      /// <summary>
   6:      /// Certificate collection.
   7:      /// </summary>
   8:      public class CertificateCollection : ConfigurationElementCollection
   9:      {
  10:          #region Methods
  11:   
  12:          /// <summary>
  13:          /// Adds the specified element.
  14:          /// </summary>
  15:          /// <param name="element">The element.</param>
  16:          public void Add(CertificateElement element)
  17:          {
  18:              this.BaseAdd(element);
  19:          }
  20:   
  21:          /// <summary>
  22:          /// Creates a new <see cref="T:System.Configuration.ConfigurationElement" />.
  23:          /// </summary>
  24:          /// <returns>
  25:          /// A new <see cref="T:System.Configuration.ConfigurationElement" />.
  26:          /// </returns>
  27:          protected override ConfigurationElement CreateNewElement()
  28:          {
  29:              return new CertificateElement();
  30:          }
  31:   
  32:          /// <summary>
  33:          /// Gets the element key for a specified configuration element.
  34:          /// </summary>
  35:          /// <param name="element">The 
  36:          ///   <see cref="T:System.Configuration.ConfigurationElement" /> to 
  37:          ///   return the key for.</param>
  38:          /// <returns>
  39:          /// An <see cref="T:System.Object" /> that acts as the key for the 
  40:          ///   specified <see cref="T:System.Configuration.ConfigurationElement" />.
  41:          /// </returns>
  42:          protected override object GetElementKey(ConfigurationElement element)
  43:          {
  44:              return ((CertificateElement)element).CertificateType;
  45:          }
  46:   
  47:          #endregion
  48:      }
  49:  }

 
The last part of the puzzle is a new class named “CertificateConfiguration” which is derived from System.Configuration.ConfigurationSection.
 

   1:  using System.Collections.Generic;
   2:  using System.Configuration;
   3:  using System.Diagnostics.CodeAnalysis;
   4:  using System.Linq;
   5:   
   6:  namespace ConfigurationLoader
   7:  {
   8:      /// <summary>
   9:      /// Used for loading values from the following configuration xml
  10:      ///     <CertificateSection authenticationMode="Test">
  11:      ///         <Certificates>
  12:      ///             <add
  13:      ///                 certificateType="cipherCertificate"
  14:      ///                 findValue="E86D56AC84D51F9C2D12C898341F5FA38E909B02"
  15:      ///                 storeLocation="LocalMachine"
  16:      ///                 storeName="TrustedPeople"
  17:      ///                 x509FindType="FindByThumbprint" />
  18:      ///              <add
  19:      ///                 certificateType="serviceCertificate"
  20:      ///                 findValue="3D25DB176A2098F010C288761F5B367F3DFA07FA"
  21:      ///                 storeLocation="LocalMachine"
  22:      ///                 storeName="TrustedPeople"
  23:      ///                 x509FindType="FindByThumbprint" />
  24:      ///             </Certificates>
  25:      ///     </CertificateSection>
  26:      /// </summary>
  27:      public class CertificateConfiguration : ConfigurationSection
  28:      {
  29:          #region Fields
  30:   
  31:          private const string _sectionNameXml = "CertificateSection";
  32:          private const string _collectionNameXml = "Certificates";
  33:          private const string _authenticationModeXml = "authenticationMode";
  34:   
  35:          private readonly IConfigurationManager _manager;
  36:   
  37:          #endregion
  38:   
  39:          #region Constructor
  40:   
  41:          /// <summary>
  42:          /// Initializes a new instance of the <see cref="CertificateConfiguration"/> class.
  43:          /// </summary>
  44:          public CertificateConfiguration()
  45:          {
  46:          }
  47:   
  48:          /// <summary>
  49:          /// Initializes a new instance of the <see cref="CertificateConfiguration" /> class.
  50:          /// Used for unit testing.
  51:          /// </summary>
  52:          /// <param name="manager">The manager.</param>
  53:          public CertificateConfiguration(IConfigurationManager manager)
  54:          {
  55:              this._manager = manager;
  56:          }
  57:   
  58:          #endregion
  59:   
  60:          #region Properties
  61:   
  62:          /// <summary>
  63:          /// Gets the element settings.
  64:          /// </summary>
  65:          [ExcludeFromCodeCoverage] // Excluded due to tight coupling with configuration file
  66:          public CertificateConfiguration Settings
  67:          {
  68:              get
  69:              {
  70:                  if (this._manager != null)
  71:                  {
  72:                      //----------------------------------------------------------------------
  73:                      // Used for unit testing
  74:                      //----------------------------------------------------------------------
  75:                      return (CertificateConfiguration)
  76:                          this._manager.GetSection(_sectionNameXml);
  77:                  }
  78:   
  79:                  return (CertificateConfiguration)
  80:                      ConfigurationManager.GetSection(_sectionNameXml);
  81:              }
  82:          }
  83:   
  84:          /// <summary>
  85:          /// Gets the certificate collection.
  86:          /// </summary>
  87:          [ConfigurationProperty(_collectionNameXml)]
  88:          [ConfigurationCollection(typeof(CertificateCollection),
  89:              AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
  90:          public CertificateCollection Certificates
  91:          {
  92:              get { return (CertificateCollection)base[_collectionNameXml]; }
  93:          }
  94:   
  95:          /// <summary>
  96:          /// Gets or sets the identity authentication mode.
  97:          /// </summary>
  98:          [ConfigurationProperty(_authenticationModeXml)]
  99:          public string AuthenticationMode
 100:          {
 101:              get { return (string)this[_authenticationModeXml]; }
 102:              set { this[_authenticationModeXml] = value; }
 103:          }
 104:   
 105:          #endregion
 106:   
 107:          #region Methods
 108:   
 109:          /// <summary>
 110:          /// Gets the certificate collection.
 111:          /// </summary>
 112:          /// <returns>
 113:          /// The collection.
 114:          /// </returns>
 115:          public IEnumerable<CertificateElement> GetCertificates()
 116:          {
 117:              var items = this.Settings.Certificates.Cast<CertificateElement>();
 118:              return items;
 119:          }
 120:   
 121:          /// <summary>
 122:          /// Gets the specified certificate.
 123:          /// </summary>
 124:          /// <param name="name">The name.</param>
 125:          /// <returns>
 126:          /// The instance.
 127:          /// </returns>
 128:          public CertificateElement GetCertificate(string name)
 129:          {
 130:              var item = this.Settings.Certificates.Cast<CertificateElement>()
 131:                  .First(c => c.CertificateType == name);
 132:              return item;
 133:          }
 134:   
 135:          #endregion
 136:      }
 137:  }

The first crucial piece of code here starts at line 87 – the ConfigurationCollection() attribute lets us read in the <Certificates> rows into the object collection.
  

[ConfigurationProperty(_collectionNameXml)]
[ConfigurationCollection(typeof(CertificateCollection),
    AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
public CertificateCollection Certificates
{
    get { return (CertificateCollection)base[_collectionNameXml]; }
}

And we use some beautiful LINQ statements to get the collection or single item back – sweet!
  

public IEnumerable<CertificateElement> GetCertificates()
{
    var items = this.Settings.Certificates.Cast<CertificateElement>();
    return items;
}
 
public CertificateElement GetCertificate(string name)
{
    var item = this.Settings.Certificates.Cast<CertificateElement>()
        .First(c => c.CertificateType == name);
    return item;
}

Note, IConfigurationManager interface is again used for Unit Testing – and will be covered in the next post.

Ok, now all we have to do is show it working – add the following to your console app…
  

   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:              //----------------------------------------------------------------------
  20:              // Load the collection of certificates
  21:              //----------------------------------------------------------------------
  22:              var certs = new CertificateConfiguration();
  23:              foreach (var certificate in certs.GetCertificates())
  24:              {
  25:                  Console.WriteLine(certificate.CertificateType);
  26:                  Console.WriteLine(certificate.FindValue);
  27:                  Console.WriteLine(certificate.StoreLocation);
  28:                  Console.WriteLine(certificate.StoreName);
  29:                  Console.WriteLine(certificate.X509FindType);
  30:                  Console.WriteLine();
  31:              }
  32:   
  33:              Console.WriteLine("----------------------");
  34:   
  35:              //----------------------------------------------------------------------
  36:              // Load a single certificate from collection
  37:              //----------------------------------------------------------------------
  38:              var certificatesSingle = certs.GetCertificate("serviceCertificate");
  39:   
  40:              Console.WriteLine(certificatesSingle.CertificateType);
  41:              Console.WriteLine(certificatesSingle.FindValue);
  42:              Console.WriteLine(certificatesSingle.StoreLocation);
  43:              Console.WriteLine(certificatesSingle.StoreName);
  44:              Console.WriteLine(certificatesSingle.X509FindType);
  45:              Console.WriteLine("----------------------");
  46:   
  47:              Console.ReadLine();
  48:          }
  49:      }
  50:  }

Now run it – and again, totally awesome – we can now get back every item in the collection, or just one.

image

If that’s not enough – in the next installment we’ll be wrapping this up adding Unit Tests! 

See you then in “Custom Configuration Pattern 103

Good coding!

…Lance

Leave a Reply