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