Flattening .NET Exceptions and Simple Hierarchies

Home / Flattening .NET Exceptions and Simple Hierarchies

Exceptions in .NET can be difficult to log or inspect due to their hierarchical nature with inner exceptions. Since the InnerException on a given exception is not an enumerated list, it requires a little bit of work to flatten the hierarchy.


Using generic extensions, it pretty easy to flatten any given object which has the structure of a .NET exception (Parent -> Child1 -> Child2 -> Child3) where each child is mapped to a single instance property on the parent. An expression passed to the extension will define which property is the child.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static class Extensions
{
    // Flatten any object of type TSource that has a single child of type TSource
    public static IEnumerable<TSource> Flatten<TSource>(this TSource source, Expression<Func<TSource, object>> expression)
    {
        var tobj = source;
 
        var memberExpression = (MemberExpression)expression.Body;
        var propertyInfo = (PropertyInfo)memberExpression.Member;
        do
        {
            yield return tobj;
            tobj = (TSource)propertyInfo.GetValue(tobj);
        }
        while (tobj != null);      
    }
}

Then, to use the extension, it might look like the below code. I’m defining a nested Exception and then using the extension and a LINQ aggregate to get a single string containing all exception messages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Main()
{
    // Create a nested exception
    var exception = new Exception("message",
        new Exception("inner message1",
            new Exception("inner message2",
                new Exception("", // No message
                    new Exception("inner message4")
                )
            )
        )
    );
             
    var message = exception
        .Flatten<Exception>(x => x.InnerException)
        .Where(x => !string.IsNullOrWhiteSpace(x.Message))
        .Select(x => x.Message)
        .Aggregate((current, next) => $"{current} | {next}");
 
    Console.WriteLine(message);
}

The nice thing about the extension method is that it is can be used for, essentially, any type rather than being limited to Exceptions specifically.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.