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”.
Now 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!
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