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.
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.
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.