ELMAH in Azure and Connection Strings business

ELMAH is a nifty tool that allows to you record exceptions that arise in your web-application. It is very handy and everyone should use it in addition to application logging. But if you are using Dependency Injection and have your connection strings provided by some service, then you might have trouble. For example, in our app, we use Azure Configuration settings and we have a service IConfiguration that has a lot of get-methods for different config settings in our app, including database connection string.

IConfiguration looks like that:

public interface IConfiguration
{
    string GetDatabaseConnectionString();
    // .. many other settings in format GetSomeSetting()
}

Implementation is like that:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure;

public class Configuration : IConfiguration
{
    public string GetDatabaseConnectionString()
    {
        return GetSetting("DatabaseConnectionString");
    }

    // the rest of the implementation as per interface

    private string GetSetting(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentNullException("key");
        }

        var result = CloudConfigurationManager.GetSetting(key);
        return result;
    }
}

CloudConfigurationManager.GetSetting(key) looks up Azure configuration file and gets the key value. If there is no Azure configuration file, it falls back to web.config and looks up ApplicationSettings with that key. So it works locally, even if you are developing your app in IIS and not deploying it to Azure emulator.

ELMAH on the other hand insists on providing connection string or name of connection string in web.config. And this would duplicate places where you must write your connection string. Adding extra work in maintenance and another place where errors can crop-in. Actually our app run for about 2 weeks with incorrect connection string for ELMAH.

So what can you do to fix this?

First of all I’ve introduced Ambient Context pattern to access our configuration object:

public static class ConfigurationContext
{
    private static IConfiguration configuration;

    public static IConfiguration Current
    {
        get
        {
            if (configuration == null)
            {
                configuration = new Configuration();
            }
            return configuration;
        }
        set
        {
            if (value == null)
            {
                throw new NullReferenceException("configuration");
            }
            configuration = value;
        }
    }

    public static void ResetToDefault()
    {
        configuration = new Configuration();
    }
}

This way I can set configuration stub for unit-testing, but the default implementation is taking data from Azure *.cscfg/web.config files. And also configuration is available anywhere as a static reference ConfigurationContext.Current.GetSomeSetting();

Elmah Override

Now, to get Elmah to take our configuration string, I had to create a class that inherits from Elmah logger:

public class ElmahCustomLog : Elmah.SqlErrorLog
{
    private readonly string connectionString;

    public ElmahCustomLog(IDictionary config)
        : base(config)
    {
        this.connectionString = ConfigurationContext.Current.GetDatabaseConnectionString();
    }

    public ElmahCustomLog(string connectionString)
        : base(connectionString)
    {
        this.connectionString = ConfigurationContext.Current.GetDatabaseConnectionString();
    }

    public override string ConnectionString
    {
        get { return this.connectionString; }
    }
}

A lot of strange things happen here. I did not come with this on a spot, it took me a while, also I had to look through Elmah code to figure out how it works with connection strings. So just trust me on this one.

To get our overriding class hooked in with the system, we need to update web.config. Find section <elmah> and replace errorLog entry with this:

<errorLog type="Web.Infrastructure.ElmahCustomLog, Web" connectionString="This value is required for our override class to operate without throwing an exception" />

type is a reference to the type of your overriding ElmahCustomLog. Format goes like this: "Namespace.ClassName, AssemblyName". And you still must provide connection string value, but it does not have to be correct value, it must be some string. If you don’t provide something for connection string Elmah will throw a null reference exception, because it will validate the settings. But don’t worry, it is only validation, the actual setting is taken from our configuration.

Now rebuild your project and check if everything is working. ELMAH is yet another library where you can remove a reference to the connection string reference in your web.config.