May 13, 2013 LANCE LARSEN
NEXT

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…

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.
 

using System.Configuration;
 
namespace ConfigurationLoader
{
    /// <summary>
    /// Certificate element.
    /// </summary>
    public class CertificateElement : ConfigurationElement
    {
        #region Fields
 
        private const string _certificateTypeXml = "certificateType";
        private const string _findValueXml = "findValue";
        private const string _storeLocationXml = "storeLocation";
        private const string _storeNameXml = "storeName";
        private const string _x509FindTypeXml = "x509FindType";
 
        #endregion
 
        #region Constructors
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CertificateElement"/> class.
        /// </summary>
        public CertificateElement()
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CertificateElement" /> class.
        /// </summary>
        /// <param name="certificateType">Type of the certificate.</param>
        /// <param name="findValue">The find value.</param>
        /// <param name="storeLocation">The store location.</param>
        /// <param name="storeName">Name of the store.</param>
        /// <param name="x509FindType">Type of the X509.</param>
        public CertificateElement(string certificateType, 
            string findValue, string storeLocation, 
            string storeName, string x509FindType)
        {
            this.CertificateType = certificateType;
            this.FindValue = findValue;
            this.StoreLocation = storeLocation;
            this.StoreName = storeName;
            this.X509FindType = x509FindType;
        }
 
        #endregion
 
        #region Properties
 
        /// <summary>
        /// Gets or sets the type of the certificate.
        /// </summary>
        [ConfigurationProperty(_certificateTypeXml)]
        public string CertificateType
        {
            get { return (string)this[_certificateTypeXml]; }
            set { this[_certificateTypeXml] = value; }
        }
 
        /// <summary>
        /// Gets or sets the find value.
        /// </summary>
        [ConfigurationProperty(_findValueXml)]
        public string FindValue
        {
            get { return (string)this[_findValueXml]; }
            set { this[_findValueXml] = value; }
        }
 
        /// <summary>
        /// Gets or sets the store location.
        /// </summary>
        [ConfigurationProperty(_storeLocationXml)]
        public string StoreLocation
        {
            get { return (string)this[_storeLocationXml]; }
            set { this[_storeLocationXml] = value; }
        }
 
        /// <summary>
        /// Gets or sets the name of the store.
        /// </summary>
        [ConfigurationProperty(_storeNameXml)]
        public string StoreName
        {
            get { return (string)this[_storeNameXml]; }
            set { this[_storeNameXml] = value; }
        }
 
        /// <summary>
        /// Gets or sets the type of the X509 find.
        /// </summary>
        [ConfigurationProperty(_x509FindTypeXml)]
        public string X509FindType
        {
            get { return (string)this[_x509FindTypeXml]; }
            set { this[_x509FindTypeXml] = value; }
        }
 
        #endregion
    }
}

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.
  

using System.Configuration;
 
namespace ConfigurationLoader
{
    /// <summary>
    /// Certificate collection.
    /// </summary>
    public class CertificateCollection : ConfigurationElementCollection
    {
        #region Methods
 
        /// <summary>
        /// Adds the specified element.
        /// </summary>
        /// <param name="element">The element.</param>
        public void Add(CertificateElement element)
        {
            this.BaseAdd(element);
        }
 
        /// <summary>
        /// Creates a new <see cref="T:System.Configuration.ConfigurationElement" />.
        /// </summary>
        /// <returns>
        /// A new <see cref="T:System.Configuration.ConfigurationElement" />.
        /// </returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new CertificateElement();
        }
 
        /// <summary>
        /// Gets the element key for a specified configuration element.
        /// </summary>
        /// <param name="element">The 
        ///   <see cref="T:System.Configuration.ConfigurationElement" /> to 
        ///   return the key for.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that acts as the key for the 
        ///   specified <see cref="T:System.Configuration.ConfigurationElement" />.
        /// </returns>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((CertificateElement)element).CertificateType;
        }
 
        #endregion
    }
}

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

using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
 
namespace ConfigurationLoader
{
    /// <summary>
    /// Used for loading values from the following configuration xml
    ///     <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>
    /// </summary>
    public class CertificateConfiguration : ConfigurationSection
    {
        #region Fields
 
        private const string _sectionNameXml = "CertificateSection";
        private const string _collectionNameXml = "Certificates";
        private const string _authenticationModeXml = "authenticationMode";
 
        private readonly IConfigurationManager _manager;
 
        #endregion
 
        #region Constructor
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CertificateConfiguration"/> class.
        /// </summary>
        public CertificateConfiguration()
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CertificateConfiguration" /> class.
        /// Used for unit testing.
        /// </summary>
        /// <param name="manager">The manager.</param>
        public CertificateConfiguration(IConfigurationManager manager)
        {
            this._manager = manager;
        }
 
        #endregion
 
        #region Properties
 
        /// <summary>
        /// Gets the element settings.
        /// </summary>
        [ExcludeFromCodeCoverage] // Excluded due to tight coupling with configuration file
        public CertificateConfiguration Settings
        {
            get
            {
                if (this._manager != null)
                {
                    //----------------------------------------------------------------------
                    // Used for unit testing
                    //----------------------------------------------------------------------
                    return (CertificateConfiguration)
                        this._manager.GetSection(_sectionNameXml);
                }
 
                return (CertificateConfiguration)
                    ConfigurationManager.GetSection(_sectionNameXml);
            }
        }
 
        /// <summary>
        /// Gets the certificate collection.
        /// </summary>
        [ConfigurationProperty(_collectionNameXml)]
        [ConfigurationCollection(typeof(CertificateCollection),
            AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
        public CertificateCollection Certificates
        {
            get { return (CertificateCollection)base[_collectionNameXml]; }
        }
 
        /// <summary>
        /// Gets or sets the identity authentication mode.
        /// </summary>
        [ConfigurationProperty(_authenticationModeXml)]
        public string AuthenticationMode
        {
            get { return (string)this[_authenticationModeXml]; }
            set { this[_authenticationModeXml] = value; }
        }
 
        #endregion
 
        #region Methods
 
        /// <summary>
        /// Gets the certificate collection.
        /// </summary>
        /// <returns>
        /// The collection.
        /// </returns>
        public IEnumerable<CertificateElement> GetCertificates()
        {
            var items = this.Settings.Certificates.Cast<CertificateElement>();
            return items;
        }
 
        /// <summary>
        /// Gets the specified certificate.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// The instance.
        /// </returns>
        public CertificateElement GetCertificate(string name)
        {
            var item = this.Settings.Certificates.Cast<CertificateElement>()
                .First(c => c.CertificateType == name);
            return item;
        }
 
        #endregion
    }
}

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…
  

using System;
using ConfigurationLoader;
 
namespace CustomConfigurationPattern
{
    class Program
    {
        static void Main()
        {
            //----------------------------------------------------------------------
            // Load a single element certificate
            //----------------------------------------------------------------------
            Console.WriteLine(CertificateConfigurationSingle.Settings.FindValue);
            Console.WriteLine(CertificateConfigurationSingle.Settings.StoreLocation);
            Console.WriteLine(CertificateConfigurationSingle.Settings.StoreName);
            Console.WriteLine(CertificateConfigurationSingle.Settings.X509FindType);
            Console.WriteLine("----------------------");
 
            //----------------------------------------------------------------------
            // Load the collection of certificates
            //----------------------------------------------------------------------
            var certs = new CertificateConfiguration();
            foreach (var certificate in certs.GetCertificates())
            {
                Console.WriteLine(certificate.CertificateType);
                Console.WriteLine(certificate.FindValue);
                Console.WriteLine(certificate.StoreLocation);
                Console.WriteLine(certificate.StoreName);
                Console.WriteLine(certificate.X509FindType);
                Console.WriteLine();
            }
 
            Console.WriteLine("----------------------");
 
            //----------------------------------------------------------------------
            // Load a single certificate from collection
            //----------------------------------------------------------------------
            var certificatesSingle = certs.GetCertificate("serviceCertificate");
 
            Console.WriteLine(certificatesSingle.CertificateType);
            Console.WriteLine(certificatesSingle.FindValue);
            Console.WriteLine(certificatesSingle.StoreLocation);
            Console.WriteLine(certificatesSingle.StoreName);
            Console.WriteLine(certificatesSingle.X509FindType);
            Console.WriteLine("----------------------");
 
            Console.ReadLine();
        }
    }
}

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

NEXT
[ SYSTEM ] Rejoining the server...
[ WARNING ] Rejoin failed... trying again in seconds.
[ CRITICAL ] Failed to rejoin. Please retry or reload the page.
[ PAUSED ] The session has been paused by the server.
[ CRITICAL ] Failed to resume the session. Please retry or reload the page.

RECONNECTING

NEURAL_LINK_LOST

Stream has been de-synced
or shifted to a restricted sector.
Attempting to re-establish connection...