Basic compression in .NET

Following on from my previous post on encryption, a similar technique often used in conjunction with encryption is compression.

As an aside, if you are going to use encryption and compression together it’s important that you compress and then encrypt rather than the other way round. This is due to the way compression engines work. They reduce data size by looking for repeating patterns in data and then storing the pattern once along with the locations in the data in which it appears. It is often the case that unencrypted data is far more repetitive than the seemingly random output you get when using encryption. This is especially true for human readable text. Consider how many times words are repeated in a piece of prose versus how many repeating patterns there are likely to be in encrypted version of the prose.

As with encryption, we’ll start with a basic interface so that compression can be injected in other objects using dependency injection:

namespace Compression
{
    public interface ICompressor
    {
        string Compress(string text);
        string Decompress(string compressedText);
    }
}

The implementation for this is as follows:

using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;

namespace Compression
{
    public class Compressor : ICompressor
    {
        public string Compress(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }

            var inputArray = StringToByteArray(value);

            using (var outputStream = new MemoryStream())
            {
                using (var compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
                {
                    // Compress:
                    compressionStream.Write(inputArray, 0, inputArray.Length);
                    // Close, but DO NOT FLUSH as this could result in data loss:
                    compressionStream.Close();

                    // Get a byte array from the output stream:
                    var outputArray = outputStream.ToArray();
                    outputStream.Close();

                    return ByteArrayToString(outputArray);
                }
            }
        }

        public string Decompress(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }

            var inputArray = StringToByteArray(value);

            using (var inputStream = new MemoryStream(inputArray))
            {
                using (var compressionStream = new GZipStream(inputStream, CompressionMode.Decompress))
                {
                    var outputList = new List<byte>();
                    int nextByte;
                    while ((nextByte = compressionStream.ReadByte()) != -1)
                    {
                        outputList.Add((byte)nextByte);
                    }

                    inputStream.Close();
                    compressionStream.Close();

                    return ByteArrayToString(outputList.ToArray());
                }
            }
        }

        private static byte[] StringToByteArray(string value)
        {
            var array = new byte[value.Length];
            for (var i = 0; i < array.Length; i++)
            {
                array[i] = (byte)value[i];
            }

            return array;
        }

        private static string ByteArrayToString(byte[] array)
        {
            var stringBuilder = new StringBuilder(array.Length);
            foreach (var b in array)
            {
                stringBuilder.Append((char)b);
            }

            return stringBuilder.ToString();
        }
    }
}

This can be demonstrated via the following simple test harness:

using System;
using Compression;

namespace TestHarness
{
    public static class Program
    {
        public static void Main()
        {
            const string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
            var compressor = new Compressor();
            var compressedText = compressor.Compress(text);
            var decompressedText = compressor.Decompress(compressedText);

            ShowText("Text", text);
            ShowText("CompressedText", compressedText);
            ShowText("DecompressedText", decompressedText);
            
            Console.ReadLine();
        }

        private static void ShowText(string label, string text)
        {
            Console.WriteLine(label + ":");
            Console.WriteLine("Length: " + text.Length);
            Console.WriteLine(text);
            Console.WriteLine();
        }
    }
}

I’m not feeling particularly creative this morning so I opted for “Lorem ipsum” rather than anything witty as my input text.

The output of the program is as follows:

compression

Note that the compressed text is 282 characters in length while the original text is 445 characters.

Extending the Compressor class to be able to compress arrays and other data would be pretty simple too, given that the input strings are converted to arrays before the compression is performed.

Basic two-way encryption in .NET

There are lots cases when we need to encrypt and then decrypt data using .NET. A great example is if we want to store something secure in a cookie. We write the encrypted data to the web-response and decrypt it again again later from the next web-request.

I’ve been using the following algorithm for two-way encryption for a number of years now. I found it on the web a long time ago, so unfortunately don’t have a link to the original source any more. If anyone can help me here that would be appreciated.

First, let’s start with an interface as the chances are we’re going to need to inject encryption capabilities into other objects using dependency injection:

namespace Encryption
{
    public interface IEncryptor
    {
        string Encrypt(string text);
        string Decrypt(string encryptedText);
    }
}

The implementation for this is as follows:

using System;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using System.Globalization;
using System.Runtime.InteropServices;

namespace Encryption
{
    public class Encryptor : IEncryptor
    {
        private readonly SecureString _password;

        public Encryptor(SecureString password)
        {
            _password = password;
        }

        public string Encrypt(string text)
        {
            return Encrypt(text, GetDefaultSalt());
        }

        public string Encrypt(string text, string salt)
        {
            if (text == null)
            {
                return null;
            }

            RijndaelManaged rijndaelCipher;
            byte[] textData;
            ICryptoTransform encryptor;

            using (rijndaelCipher = new RijndaelManaged())
            {
                var secretKey = GetSecretKey(salt);

                // First we need to turn the input strings into a byte array.
                textData = Encoding.Unicode.GetBytes(text);

                // Create a encryptor from the existing secretKey bytes.
                // We use 32 bytes for the secret key. The default Rijndael 
                // key length is 256 bit (32 bytes) and then 16 bytes for the 
                // Initialization Vector (IV). The default Rijndael IV length is 
                // 128 bit (16 bytes).
                encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
            }

            MemoryStream memoryStream;
            byte[] encryptedData;

            // Create a MemoryStream that is going to hold the encrypted bytes:
            using (memoryStream = new MemoryStream())
            {
                // Create a CryptoStream through which we are going to be processing 
                // our data. CryptoStreamMode.Write means that we are going to be 
                // writing data to the stream and the output will be written in the 
                // MemoryStream we have provided.
                using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    // Start the encryption process.
                    cryptoStream.Write(textData, 0, textData.Length);

                    // Finish encrypting.
                    cryptoStream.FlushFinalBlock();

                    // Convert our encrypted data from a memoryStream into a byte array.
                    encryptedData = memoryStream.ToArray();

                    // Close both streams.
                    memoryStream.Close();
                    cryptoStream.Close();
                }
            }

            // Convert encrypted data into a base64-encoded string.
            // A common mistake would be to use an Encoding class for that.
            // It does not work, because not all byte values can be
            // represented by characters. We are going to be using Base64 encoding.
            // That is designed exactly for what we are trying to do.
            var encryptedText = Convert.ToBase64String(encryptedData);

            // Return encrypted string.
            return encryptedText;
        }

        public string Decrypt(string encryptedText)
        {
            return Decrypt(encryptedText, GetDefaultSalt());
        }

        public string Decrypt(string encryptedText, string salt)
        {
            if (encryptedText == null)
            {
                return null;
            }

            RijndaelManaged rijndaelCipher;
            byte[] encryptedData;
            ICryptoTransform decryptor;

            using (rijndaelCipher = new RijndaelManaged())
            {
                var secretKey = GetSecretKey(salt);

                // First we need to turn the input strings into a byte array.
                encryptedData = Convert.FromBase64String(encryptedText);

                // Create a decryptor from the existing SecretKey bytes.
                decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
            }

            MemoryStream memoryStream;
            byte[] unencryptedData;
            int decryptedDataLength;

            using (memoryStream = new MemoryStream(encryptedData))
            {
                // Create a CryptoStream. Always use Read mode for decryption.
                using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    // Since at this point we don't know what the size of decrypted data
                    // will be, allocate the buffer long enough to hold EncryptedData;
                    // DecryptedData is never longer than EncryptedData.
                    unencryptedData = new byte[encryptedData.Length];

                    // Start decrypting.
                    try
                    {
                        decryptedDataLength = cryptoStream.Read(unencryptedData, 0, unencryptedData.Length);
                    }
                    catch
                    {
                        throw new CryptographicException("Unable to decrypt string");
                    }                    

                    cryptoStream.Close();
                    memoryStream.Close();
                }
            }

            // Convert decrypted data into a string.
            var decryptedText = Encoding.Unicode.GetString(unencryptedData, 0, decryptedDataLength);

            // Return decrypted string.  
            return decryptedText;
        }

        private PasswordDeriveBytes GetSecretKey(string salt)
        {
            // We are using salt to make it harder to guess our key
            // using a dictionary attack.
            var encodedSalt = Encoding.ASCII.GetBytes(salt);

            var valuePointer = IntPtr.Zero;
            try
            {
                // The Secret Key will be generated from the specified
                // password and salt.
                valuePointer = Marshal.SecureStringToGlobalAllocUnicode(_password);
                return new PasswordDeriveBytes(Marshal.PtrToStringUni(valuePointer), encodedSalt);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(valuePointer);
            }            
        }

        private string GetDefaultSalt()
        {
            return _password.Length.ToString(CultureInfo.InvariantCulture);
        }
    }
}

This can then be used as follows:

using System;
using System.Security;

namespace Encryption
{
    public static class Program
    {
        public static void Main()
        {
            using (var password = GetPassword())
            {
                var encryptor = new Encryptor(password);

                const string text = "Hello World!";

                var encryptedText = encryptor.Encrypt(text);
                var decryptedText = encryptor.Decrypt(encryptedText);

                Console.WriteLine(encryptedText);
                Console.WriteLine(decryptedText);

                Console.ReadLine();
            }
        }

        private static SecureString GetPassword()
        {
            var password = new SecureString();

            password.AppendChar((char)80); // P
            password.AppendChar((char)97); // a
            password.AppendChar((char)115); // s
            password.AppendChar((char)115); // s
            password.AppendChar((char)119); // w
            password.AppendChar((char)111); // o
            password.AppendChar((char)114); // r
            password.AppendChar((char)100); // d
            password.AppendChar((char)49); // 1

            return password;
        }
    }
}

Note that we store the password using the SecureString class. This makes it harder to read the string from the memory while the program is running in the event that the host machine is hacked. Also, as I’m super-paranoid, I write to the SecureString using ASCII values converted to chars to prevent any readable values from appearing in string tables in the compiled code.

When using the Encryptor class you also have the option to add “salt”. If no salt is supplied a default based on the length of the password is used. Salt is effectively an additional piece of information you must supply in order to decrypt information, which becomes part of the decryption password. It is recommended that you use salt wherever possible as it makes it much harder for a hacker to decrypt sets of data using brute-force attacks.

Happy encrypting!

Serialising constructor-less .NET types (e.g. MailAddress) using Newtonsoft.Json.JsonConvert in the Json.NET library

Newtonsoft’s Json.NET library is brilliant for serialising .NET objects into JSON and back again. However, there are some things it can’t do. For instance, if we try serialising an object based on the following class:

using System.Net.Mail;

namespace JsonConvertors
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public MailAddress EmailAddress { get; set; }
    }
}

…using the following code:

var person = new Person
{
    FirstName = "Joe",
    LastName = "Bloggs",
    EmailAddress = new MailAddress("joe@bloggs.com"),
};

var json = JsonConvert.SerializeObject(person);
var personCopy = JsonConvert.DeserializeObject<Person>(json);

We get the following nasty error:

Unable to find a constructor to use for type System.Net.Mail.MailAddress. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'EmailAddress.DisplayName', line 1, position 69.

Why Microsoft didn’t add a constructor to the MailAddress type is beyond me. Of course, we could just store the EmailAddress as a string and bypass the issue that way, but I am a firm believer that everything should be strongly typed. Fortunately, we can use the JsonConverter class to tell JsonConvert how to handle constructor-less types like MailAddress.

Here’s how to write a converter for the MailAddress type:

using System;
using System.Net.Mail;
using Newtonsoft.Json;

namespace JsonConvertors
{
    public class MailAddressConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var mailAddress = value as MailAddress;
            writer.WriteValue(mailAddress == null? string.Empty : mailAddress.ToString());
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var text = reader.Value as string;
            MailAddress mailAddress;

            return IsValidMailAddress(text, out mailAddress) ? mailAddress : null;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(MailAddress);
        }

        private static bool IsValidMailAddress(string text, out MailAddress value)
        {
            try
            {
                value = new MailAddress(text);
                return true;
            }
            catch
            {
                value = null;
                return false;
            }
        }
    }
}

Note that care has been taken to handle null and non-convertible values.

Then simply add the following attribute to your Person class:

using System.Net.Mail;
using Newtonsoft.Json;

namespace JsonConvertors
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [JsonConverter(typeof(MailAddressConverter))]
        public MailAddress EmailAddress { get; set; }
    }
}

…and the conversion will work like a charm.

Configure the .NET SmtpClient object using the configuration file

If you’re using the SmtpClient class in the System.Net.Mail namespace of the .NET Framework, there’s an easy way to set the properties of the client without having to wire them up in the code manually.

Simply add the following section to your application configuration file and the class will auto-magically wire up the properties at run-time:

<system.net>
  <mailSettings>
    <smtp deliveryMethod="Network">
      <network enableSsl="true" port="123" host="myhost" defaultCredentials="false" userName="myuser" password="mypassword"/>
    </smtp>
  </mailSettings>
</system.net>

There are a whole bunch more settings you can edit. See Microsoft’s <mailSettings> Element documentation page for more details.

Text truncation at a word boundary using C#

Following on from my last post on stripping HTML from text using C#, once I had removed all signs of HTML from the incoming text, I was also required to show a short preview of the text. I originally went with a truncation method, as follows:

namespace ExtensionMethods
{
    public static class StringExtensionMethods
    {
        public static string Truncate(this string text, int maximumLength)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            return text.Length <= maximumLength ? text : text.Substring(0, maximumLength);
        }
    }
}

This works, but the results look a little odd if the truncate happens half-way through a word.

Instead, I came up this method to truncate at the first word break within the allowed number of characters:

using System.Linq;

namespace ExtensionMethods
{
    public static class StringExtensionMethods
    {
        private static readonly char[] Punctuation = {'.', ',', ';', ':'};

        public static string TruncateAtWordBoundary(this string text, int maximumLength)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            if (text.Length <= maximumLength)
            {
                return text;
            }

            // If the character after the cut off is white space or punctuation 
            // then return what we've got using substring:
            var isCutOffWhiteSpaceOrPunctuation = char.IsWhiteSpace(text[maximumLength]) || Punctuation.Contains(text[maximumLength]);
            text = text.Substring(0, maximumLength);

            if (isCutOffWhiteSpaceOrPunctuation)
            {
                return text;
            }

            // Find the last white-space or punctuation and chop off there:
            var lastWhiteSpaceOrPunctuationPosition = 0;
            for (var i = text.Length - 1; i >= 0; i--)
            {
                if (char.IsWhiteSpace(text[i]) || Punctuation.Contains(text[i]))
                {
                    lastWhiteSpaceOrPunctuationPosition = i;
                    break;
                }
            }

            text = text.Substring(0, lastWhiteSpaceOrPunctuationPosition).Trim();

            return text;
        }
    }
}

While not perfect, this approach works a lot better. Please feel free to suggest improvements.

Beware! ASP.NET MVC ActionFilterAttributes are cached between requests!

My colleagues and I recently encountered a very strange bug in an ASP.NET MVC web-site which seemed to be to do with NHibernate. The error suggested that we were trying to use a session that had already been closed. We could not find any sign of a closed session anywhere within the controllers in our project. Eventually we pinned the error down to a repository (containing an NHibernate session) referenced in an ActionFilterAttribute. The guy that wrote the code assumed that the repository would be referenced freshly by the attribute each time it was used, but of course it wasn’t; the .NET engine was helpfully caching the attribute between calls so that some users were getting sessions intended for other users.

The moral of the story is to make sure your ActionFilterAttributes are always stateless.

The following simple demonstration highlights this. Here is an ActionFilterAttribute which stores the date and time in a field on construction:

using System;
using System.Web.Mvc;

namespace WebTest.Attributes
{
    public class TestActionFilterAttribute : ActionFilterAttribute
    {
        private readonly DateTime _now;

        public TestActionFilterAttribute()
        {
            _now = DateTime.Now;
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Put the date into a ViewBag property:
            filterContext.Controller.ViewBag.DateTime = _now;
        }
    }
}

This is then used in a controller as follows:

using System;
using System.Text;
using System.Web.Mvc;
using WebTest.Attributes;

namespace WebTest.Controllers
{
    public class HomeController : Controller
    {
        [TestActionFilter]
        public ActionResult Index()
        {
            const string dateTimeFormat = "yyyy-MM-dd HH:mm:ss.fffffff";

            var text = new StringBuilder();

            text.AppendFormat("DateTime called directly: {0}<br/>", DateTime.Now.ToString(dateTimeFormat));
            text.AppendFormat("DateTime taken from ActionFilterAttribute: {0}<br/>", ViewBag.DateTime.ToString(dateTimeFormat));

            return Content(text.ToString());
        }
    }
}

If you refresh the page a few times you’ll notice that the time coming from the ViewBag (which originates from the attribute) remains the same while the current time continues to increase, proving that the attribute isn’t being instantiated for each web call. You have been warned!

Stripping HTML from text using C#

I recently had a situation where I needed to show some text received in HTML format as plain text. This is the method I now use for this purpose, implemented as an extension method:

using System.Linq;
using System.Text.RegularExpressions;

namespace ExtensionMethods
{
    public static class StringExtensionMethods
    {
        public static string StripHtml(this string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            var tagRegex = new Regex(@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>");
            var tagMatches = tagRegex.Matches(text);

            var commentRegex = new Regex(@"\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>");
            var commentMatches = commentRegex.Matches(text);

            // Replace each tag match with an empty space:
            text = tagMatches.Cast<object>().Aggregate(text, (current, match) => current.Replace(match.ToString(), " "));

            // Replace each comment with an empty string:
            text = commentMatches.Cast<object>()
                .Aggregate(text, (current, match) => current.Replace(match.ToString(), string.Empty));

            // We also need to replace &nbsp; as this can mess up the system:
            text = text.Replace("&nbsp;", " ");

            // Trim and remove all double spaces:
            text = text.Trim().RemoveDoubleSpaces();

            return text;
        }

        public static string RemoveDoubleSpaces(this string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            // Condense all double spaces to a single space:
            while (text.Contains("  "))
            {
                text = text.Replace("  ", " ");
            }

            return text;
        }
    }
}

The method RemoveDoubleSpaces was also needed, since after replacing HTML elements with empty space it is possible to end up with multiple empty spaces where a single space would do. This is quite a useful method in its own right, hence separating it out.

If you find any inputs which trip this method up, please let me know.

Using Visual Studio to run web-sites locally using real domains

Visual Studio is great for developing and testing web-sites, but by default it runs them under a localhost address using a randomly generated port number, as given in the property page for the web-site:

Visual Studio Real Domains 01

This is fine, but sometimes it’s useful to have sites run at a location that looks more like a real internet URL, especially if you plan on including functionality that manipulates the URL for redirects etc… I’ve also encountered issues creating cookies using the default http://localhost:xxxx address.

Fortunately, there’s a hosts file trick you can use to make Visual Studio run sites under more realistic URLs. First point 127.0.0.1 at the URL you want to use in your hosts file, for example:

127.0.0.1 www.testing.com

Note though that if the URL you are using points to a real web-site on the internet, you won’t be able to access that site again until you remove the entry from your host file.

Then edit the property pages so that you’re using the Visual Studio Development Server, a specific port (rather than an auto-assigned port) and a custom start URL containing your chosen port number and test URL, for example:

http://www.testing.com:1455

Your property pages should look like this:

Visual Studio Real Domains 02

Then, when you run your web-site using Visual Studio, it will use http://www.testing.com:1455 as the base URL. All paths relative to that should work as expected:

Visual Studio Real Domains 03

Exotic .NET XML configuration file sections

The .NET framework makes application configuration really simple by supplying an out-of-the-box XML configuration file structure and associated classes to access the information contained within it.

By default, it allows configuration key-value pairs to be set and retrieved easily at run time. For example, the following configuration settings:

  <appSettings>
    <add key="ServerName" value="WinDB1"/>
    <add key="DatabaseName" value="MyData"/>
    <add key="EnableFeature" value="true"/>
  </appSettings>

…can be retrieved using:

var appSettingsReader = new AppSettingsReader();

var serverName = (string)appSettingsReader.GetValue("ServerName", typeof(string));
var databaseName = (string)appSettingsReader.GetValue("DatabaseName", typeof(string));
var enableFeature = (bool)appSettingsReader.GetValue("EnableFeature", typeof(bool));

…or (if you add a reference to System.Configuration):

var serverName = ConfigurationManager.AppSettings["ServerName"];
var databaseName = ConfigurationManager.AppSettings["DatabaseName"];
var enableFeature = bool.Parse(ConfigurationManager.AppSettings["EnableFeature"]);

…although as per a past post of mine, Encapsulated and strongly-typed access to .NET configuration files I don’t recommend that you litter your code with references to AppSettingsReader (or ConfigurationManager), but instead that you wrap all configuration up in a class so that settings are encapsulated away.

It is often necessary to hold more complex configurations though. Many developers I’ve worked with have tried to use the key-value model to store such settings, using a single string with various delimiters to hold separate different values. For instance, given a person construct with an ID, title, first name, last name and birth date attributes, some developers would be tempted to do something like this:

  <appSettings>
    <add key="Person1" value="I:1|T:Mr|F:Joe|L:Bloggs|D:1980-01-01"/>
  </appSettings>

…and then use string split functions to populate a Person class when reading information from the configuration file. While this approach works, it results in brittle, hard to read configuration files and complex string logic to take strings apart.

Instead, it is better to use the exotic configuration capabilities included in the framework. Here is an example demonstrating how you might store configuration information to populate the following Person class:

using System;

namespace ConfigurationExample
{
    public class Person
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
    }
}

The configuration file to store such settings might look like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
  <configSections>
    <section name="people" type="ConfigurationExample.PeopleConfigurationSectionHandler, ConfigurationExample" />
  </configSections>
  
  <people>
    
    <person id="1">
      <title>Mr</title>
      <firstName>Joe</firstName>
      <lastName>Bloggs</lastName>
      <birthDate>
        <year>1980</year>
        <month>1</month>
        <day>1</day>
      </birthDate>
    </person>

    <person id="2">
      <title>Miss</title>
      <firstName>Jane</firstName>
      <lastName>Black</lastName>
      <birthDate>
        <year>1984</year>
        <month>2</month>
        <day>3</day>
      </birthDate>
    </person>
    
  </people>
  
</configuration>

A class implementing IConfigurationSectionHandler must be written to read this exotic configuration file structure; you will need a reference to System.Configuration in order to use this interface. The location of your class is given in the ‘configSections’ declaration at the top of the file. In this case the framework is being instructed to look for a class with name and namespace ‘ConfigurationExample.PeopleConfigurationSectionHandler’ in the ‘ConfigurationExample’ DLL/project in order to read the configuration section named ‘people’.

That class could look something like this:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Xml;

namespace ConfigurationExample
{
    public class PeopleConfigurationSectionHandler : IConfigurationSectionHandler
    {
        public object Create(object parent, object configContext, XmlNode section)
        {
            var people = new List<Person>();

            foreach (XmlNode node in section.ChildNodes)
            {
                if (node.NodeType == XmlNodeType.Comment)
                {
                    continue;
                }

                var id = GetAttributeInt(node, "id");
                var title = GetChildNodeString(node, "title");
                var firstName = GetChildNodeString(node, "firstName");
                var lastName = GetChildNodeString(node, "lastName");

                var birthDateNode = GetChildNode(node, "birthDate");

                var birthDateYear = GetChildNodeInt(birthDateNode, "year");
                var birthDateMonth = GetChildNodeInt(birthDateNode, "month");
                var birthDateDay = GetChildNodeInt(birthDateNode, "day");

                people.Add(new Person
                {
                    Id = id,
                    Title = title,
                    FirstName = firstName,
                    LastName = lastName,
                    BirthDate = new DateTime(birthDateYear, birthDateMonth, birthDateDay),
                });
            }

            return people;
        }

        private static string GetAttributeString(XmlNode node, string attributeName)
        {
            try
            {
                return node.Attributes[attributeName].Value;
            }
            catch
            {
                var message = string.Format("Could not read attribute named '{0}' in people section of configuration file", attributeName);
                throw new ConfigurationErrorsException(message);
            }
        }

        private static int GetAttributeInt(XmlNode node, string attributeName)
        {
            try
            {
                var value = GetAttributeString(node, attributeName);
                return int.Parse(value);
            }
            catch
            {
                var message = string.Format("Could not convert value stored in attribute named '{0}' to an integer", attributeName);
                throw new ConfigurationErrorsException(message);
            }
        }

        private static XmlNode GetChildNode(XmlNode parentNode, string nodeName)
        {
            try
            {
                return parentNode[nodeName];
            }
            catch
            {
                var message = string.Format("Could not find node named '{0}' in people section of configuration file", nodeName);
                throw new ConfigurationErrorsException(message);
            }
        }

        private static string GetChildNodeString(XmlNode parentNode, string nodeName)
        {
            try
            {
                return parentNode[nodeName].InnerText;
            }
            catch
            {
                var message = string.Format("Could not read node named '{0}' in people section of configuration file", nodeName);
                throw new ConfigurationErrorsException(message);
            }
        }

        private static int GetChildNodeInt(XmlNode parentNode, string nodeName)
        {
            try
            {
                var value = GetChildNodeString(parentNode, nodeName);
                return int.Parse(value);
            }
            catch
            {
                var message = string.Format("Could not convert value stored in node named '{0}' to an integer", nodeName);
                throw new ConfigurationErrorsException(message);
            }
        }
    }
}

Although there is quite a lot of code there, it’s all pretty simple. More importantly, it’s not too brittle and very simple to extend should you wish to add more attributes to the Person class.

The method required by the IConfigurationSectionHandler interface is Create:

public object Create(object parent, object configContext, XmlNode section)

This is what gets called when the framework asks for the contents of the ‘people’ section. The ‘section’ parameter contains the people node from the XML. In essence, the code creates a list in which Person objects are stored, and loops over the child nodes in order to extract the correct information.

There are two important points to note about the code:

1) The code that detects for the presence of XML comments:

if (node.NodeType == XmlNodeType.Comment)
{
    continue;
}

…ensures that developers don’t break the code by adding comments into the XML.

2) All the helper methods in the class (e.g. GetAttributeString, GetChildNodeString, etc…) must expect there to be issues with the XML and throw meaningful, self-explanatory exceptions when things do go wrong. When there are problems with these types of sections it is not always obvious what the problem is from the default exceptions .NET throws (particularly if you’re using this technique in the start up code of a Windows Service) so you will save yourself a lot of head scratching further down the line by writing good error messages. The chances are that you will have more than one section handler in your project so it’s a good idea to encapsulate such helper methods away in an abstract ConfigurationSectionHandler base class to cut down on duplication and to promote reuse.

Assuming you have registered your section handlers correctly in the ‘configSections’ declaration, you can retrieve the Person objects as follows:

var people = (IList<Person>)ConfigurationManager.GetSection("people");

Since the IConfigurationSectionHandler interface uses the ‘object’ type as its return value you need to cast the result to the correct type. It would have been nice if the interface was made generic, but I guess you can’t have everything!

If you’re following the advice in my previous post on configuration encapsulation, you’ll probably want to wrap this call up in your ConfigurationFile class, as follows:

using System.Collections.Generic;
using System.Configuration;

namespace ConfigurationExample
{
    public class ConfigurationFile
    {
        private readonly IList<Person> people;

        public ConfigurationFile()
        {
            people = (IList<Person>)ConfigurationManager.GetSection("people");
        }

        public IList<Person> People
        {
            get
            {
                return people;
            }
        }
    }
}

Note that a reference to the Person list is stored when the class constructs to save the running the cast each time you want to retrieve the contents of the configuration file section.

In summary, it’s a little more work to write exotic configuration file sections, but the advantages over brittle, difficult to extend key-value implementations is certainly worth the extra effort.

NHibernate and log4net

Here’s a quick trick for people using NHibernate and log4net within their .NET applications. (…and if you’re not using NHibernate or log4net, you should seriously think about it!)

Add the following to your log4net configuration to find out what’s going on inside NHibernate:

  <logger name="NHibernate">
    <level value="DEBUG" />
  </logger>
  <logger name="NHibernate.SQL">
    <level value="DEBUG" />
  </logger>

As those kind folks at NHibernate have already set up the loggers behind the scenes, everything just works. The SQL logger is especially useful as it’ll show you explicitly which SQL statements are being generated by NHibernate. You can adjust the levels of NHibernate logging independently of your normal application logging, which is useful as the NHibernate logs tend to be pretty verbose.

The full configuration will look something like this:

<log4net>
  <appender name="TraceAppender" type="log4net.Appender.TraceAppender" >
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="%d %-5p- %m%n" />
    </layout>
  </appender>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="Application.log"/>
    <appendToFile value="true"/>
    <rollingStyle value="Date"/>
    <datePattern value="yyyy-MM-dd'.log'"/>
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
    </layout>
  </appender>
  <root>
    <level value="DEBUG"/>
    <appender-ref ref="TraceAppender"/>
    <appender-ref ref="RollingFileAppender"/>
  </root>
  <logger name="NHibernate">
    <level value="ERROR" />
  </logger>
  <logger name="NHibernate.SQL">
    <level value="ERROR" />
  </logger>
</log4net>