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!
Very 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.
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
