Rory Primrose

Learn from my mistakes, you don't have time to make them yourself

View project on GitHub

Using Lambda expressions to create generic exception handling

I’m writing a system that exposes several services. Each service is created using Unity and has many dependency slices including business layers, data access layers, caching, logging and exception management.

As I was writing the exception management slice, the code started to get incredibly duplicated. Each method in the class began to look like this:

public String DoSomething(String withSomeValue, Boolean andAnotherValue)
{
    try
    {
        return Dependency.DoSomething(withSomeValue, andAnotherValue);
    }
    catch (BusinessFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (SystemFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (Exception ex)
    {
        LogEvent record = new LogEvent(LogEventCategory.Exception, "We failed to do something", EventLogEntryType.Error);
    
        record.Parameters.Add("SomeValue", withSomeValue);
        record.Parameters.Add("AnotherValue", andAnotherValue);
        record.Parameters.Add("Exception", ex);
    
        LogWriter.WriteEvent(record);
    
        // Throw this as a system failure exception to indicate that the exception has been handled by the system
        throw new SystemFailureException(ex);
    }
}

When there are several methods on the interface/class, this ends up being a maintenance nightmare and really breaks the DRY principle.

It then occurred to me that I could use lambda expressions and pass the functions around as Func<T> and Action parameters. This resulted in code that looked like the following.

public String DoSomething(String withSomeValue, Boolean andAnotherValue)
{
    return InvokeWithLogging(() => Dependency.DoSomething(withSomeValue, andAnotherValue), "Failed to do something");
}
    
private T InvokeWithLogging<T>(Func<T> method, String failureMessage)
{
    try
    {
        return method.Invoke();
    }
    catch (BusinessFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (SystemFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (Exception ex)
    {
        LogEvent record = new LogEvent(LogEventCategory.Exception, failureMessage, EventLogEntryType.Error);
    
        List<FieldInfo> fields = method.Target.GetType().GetFields().Skip(1).ToList();
    
        fields.ForEach(f => record.Parameters.Add(f.Name, f.GetValue(method.Target)));
    
        record.Parameters.Add("Exception", ex);
    
        LogWriter.WriteEvent(record);
    
        // Throw this as a system failure exception to indicate that the exception has been handled by the system
        throw new SystemFailureException(ex);
    }
}

The next bit of duplication was that I have void methods and methods that return a value. I first created a duplicate InvokeWIthLogging method that accepted Action method and duplicated the exception management and logging. I wasn’t happy with this duplication so I figured that the ultimate outcome would be to get the void method to be passed to the InvokeWithLogging(Func<T>) method.

So the big question is how do you pass an Action as a Func<T>. This is really easy. Here is the final code.

public void DoSomethingElse(String withThisValue)
{
    InvokeWithLogging(() => Dependency.DoSomethingElse(withThisValue), "Failed to do something");
}
    
public String DoSomething(String withSomeValue, Boolean andAnotherValue)
{
    return InvokeWithLogging(() => Dependency.DoSomething(withSomeValue, andAnotherValue), "Failed to do something");
}
    
private void InvokeWithLogging(Action method, String failureMessage)
{
    InvokeWithLogging(() => MethodReturnWrapper(method), failureMessage);
}
    
private static Object MethodReturnWrapper(Action method)
{
    method.Invoke();
    
    return null;
}
    
private T InvokeWithLogging<T>(Func<T> method, String failureMessage)
{
    try
    {
        return method.Invoke();
    }
    catch (BusinessFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (SystemFailureException)
    {
        // Ignore this exception
        throw;
    }
    catch (Exception ex)
    {
        LogEvent record = new LogEvent(LogEventCategory.Exception, failureMessage, EventLogEntryType.Error);
    
        List<FieldInfo> fields = method.Target.GetType().GetFields().Skip(1).ToList();
    
        fields.ForEach(f => record.Parameters.Add(f.Name, f.GetValue(method.Target)));
    
        record.Parameters.Add("Exception", ex);
    
        LogWriter.WriteEvent(record);
    
        // Throw this as a system failure exception to indicate that the exception has been handled by the system
        throw new SystemFailureException(ex);
    }
}

The trick here is to pass the Action method to a function that simply returns null. This function can then be used to pass to the InvokeWithLogging(Func<T>) method. Job done.

Given that this is very generic code, this can then be pushed out into a helper class that all service exception management and logging slices can use.

Written on April 9, 2010