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.