Is there any way to write a LINQ style \"short hand\" code for walking to all levels of InnerException(s) of Exception thrown? I would prefer to write it in place instead of
You don't need extension methods or recursive calls:
try {
// Code that throws exception
}
catch (Exception e)
{
var messages = new List<string>();
do
{
messages.Add(e.Message);
e = e.InnerException;
}
while (e != null) ;
var message = string.Join(" - ", messages);
}
I don't think so, exception is not an IEnumerable so you can't perform a linq query against one on its own.
An extension method to return the inner exceptions would work like this
public static class ExceptionExtensions
{
public static IEnumerable<Exception> InnerExceptions(this Exception exception)
{
Exception ex = exception;
while (ex != null)
{
yield return ex;
ex = ex.InnerException;
}
}
}
you could then append all the messages using a linq query like this:
var allMessageText = string.Concat(exception.InnerExceptions().Select(e => e.Message + ","));
Unfortunately LINQ doesn't offer methods that could process hierarchical structures, only collections.
I actually have some extension methods that could help do this. I don't have the exact code in hand but they're something like this:
// all error checking left out for brevity
// a.k.a., linked list style enumerator
public static IEnumerable<TSource> FromHierarchy<TSource>(
this TSource source,
Func<TSource, TSource> nextItem,
Func<TSource, bool> canContinue)
{
for (var current = source; canContinue(current); current = nextItem(current))
{
yield return current;
}
}
public static IEnumerable<TSource> FromHierarchy<TSource>(
this TSource source,
Func<TSource, TSource> nextItem)
where TSource : class
{
return FromHierarchy(source, nextItem, s => s != null);
}
Then in this case you could do this to enumerate through the exceptions:
public static string GetaAllMessages(this Exception exception)
{
var messages = exception.FromHierarchy(ex => ex.InnerException)
.Select(ex => ex.Message);
return String.Join(Environment.NewLine, messages);
}
Most solutions presended here have the following implementation errors:
null
exceptionsAggregateException
A better implementation is this here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static string AggregateMessages(this Exception ex) =>
ex.GetInnerExceptions()
.Aggregate(
new StringBuilder(),
(sb, e) => sb.AppendLine(e.Message),
sb => sb.ToString());
public static IEnumerable<Exception> GetInnerExceptions(this Exception ex, int maxDepth = 5)
{
if (ex == null || maxDepth <= 0)
{
yield break;
}
yield return ex;
if (ex is AggregateException ax)
{
foreach(var i in ax.InnerExceptions.SelectMany(ie => GetInnerExceptions(ie, maxDepth - 1)))
yield return i;
}
foreach (var i in GetInnerExceptions(ex.InnerException, maxDepth - 1))
yield return i;
}
Example usage:
try
{
// ...
}
catch(Exception e)
{
Log.Error(e, e.AggregateMessages());
}
public static class ExceptionExtensions
{
public static IEnumerable<Exception> GetAllExceptions(this Exception ex)
{
Exception currentEx = ex;
yield return currentEx;
while (currentEx.InnerException != null)
{
currentEx = currentEx.InnerException;
yield return currentEx;
}
}
public static IEnumerable<string> GetAllExceptionAsString(this Exception ex)
{
Exception currentEx = ex;
yield return currentEx.ToString();
while (currentEx.InnerException != null)
{
currentEx = currentEx.InnerException;
yield return currentEx.ToString();
}
}
public static IEnumerable<string> GetAllExceptionMessages(this Exception ex)
{
Exception currentEx = ex;
yield return currentEx.Message;
while (currentEx.InnerException != null)
{
currentEx = currentEx.InnerException;
yield return currentEx.Message;
}
}
}
For those, who are waiting for a one-liner.
exc.ToString();
This will go through all your inner exceptions and return all messages, the downside is that it will also contain stack traces, etc.