Reputation: 3274
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 calling an extension function (as below) or inheriting the Exception
class.
static class Extensions
{
public static string GetaAllMessages(this Exception exp)
{
string message = string.Empty;
Exception innerException = exp;
do
{
message = message + (string.IsNullOrEmpty(innerException.Message) ? string.Empty : innerException.Message);
innerException = innerException.InnerException;
}
while (innerException != null);
return message;
}
};
Upvotes: 110
Views: 76523
Reputation: 505
How about this code:
private static string GetExceptionMessages(this Exception e, string msgs = "")
{
if (e == null) return string.Empty;
if (msgs == "") msgs = e.Message;
if (e.InnerException != null)
msgs += "\r\nInnerException: " + GetExceptionMessages(e.InnerException);
return msgs;
}
Usage:
Console.WriteLine(e.GetExceptionMessages())
Example of output:
There was no endpoint listening at http://nnn.mmm.kkk.ppp:8000/routingservice/router that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
InnerException: Unable to connect to the remote server
InnerException: No connection could be made because the target machine actively refused it 127.0.0.1:8000
Upvotes: 36
Reputation: 238
With AggregateException
public static List<Exception> GetInnerExceptions(this Exception e)
{
List<Exception> eList = new List<Exception>();
if (e is AggregateException)
{
eList.AddRange((e as AggregateException).InnerExceptions);
}
else
{
eList.Add(e);
}
List<Exception> ieList = eList
.Where(i => i.InnerException != null)
.SelectMany(i => i.InnerException.GetInnerExceptions())
.ToList();
eList.AddRange(ieList);
return eList;
}
Upvotes: 2
Reputation: 3797
I used combination of Select and Join:
Unit test:
[Test]
public void FirendlyErrorMessage_Tests()
{
// Arrange
Exception ex = new AggregateException(new Exception("MY_ERROR_MESSAGE_FROM_DCE_1"), new Exception("MY_ERROR_MESSAGE_FROM_DCE_2"), new Exception("MY_ERROR_MESSAGE_FROM_DCE_3"));
// Assert
var e = Assert.Throws<Exception>(() => ErrorHandler.RaiseFirendlyErrorMessage(ex));
Assert.AreEqual(e.Message, "One or more errors occurred. Possible reasons: MY_ERROR_MESSAGE_1, MY_ERROR_MESSAGE_2, MY_ERROR_MESSAGE_3");
}
ErrorHandler class:
public void RaiseFirendlyErrorMessage(Exception ex)
{
if (ex is AggregateException)
{
var aggrEx = ex as AggregateException;
string aggregateExcMessage = ex.Message + $" Possible reasons: { string.Join(", ", aggrEx.InnerExceptions.Select(s => s.Message)) }";
throw new Exception(aggregateExcMessage);
}
}
Final message will be:
"One or more errors occurred. Possible reasons: MY_ERROR_MESSAGE_1, MY_ERROR_MESSAGE_2, MY_ERROR_MESSAGE_3"
Upvotes: 0
Reputation: 11
After 9+ years the original question still begs for an answer.
Not exactly short, but yes, it can be done LINQ-style in a single statement:
var ex1 = new NullReferenceException("EX1");
var ex2 = new InvalidCastException("EX2", ex1);
var ex3 = new InvalidOperationException("EX3", ex2);
const int maxDepth = 10;
var message = Enumerable.Range(1, maxDepth).Aggregate(
new { s = $"{ex3.GetType().Name} - {ex3.Message}", ex = ex3.InnerException },
(v, i) => v.ex != null
? new { s = v.s + $"\nInner exception {i}: {v.ex.GetType().Name} - {v.ex.Message}",
ex = v.ex.InnerException }
: new { s = v.s, ex = (Exception)null },
v => v.s);
/* message is:
InvalidOperationException - EX3
Inner exception 1: InvalidCastException - EX2
Inner exception 2: NullReferenceException - EX1
*/
The key is to use Enumerable.Range().Aggregate() for iteration and a value v
of anonymous type (introduced in C# 3.0) holding both
v.s
being built up, as well asv.ex
as we're walking down the list.(StringBuilder left out to reduce clutter.)
Upvotes: 0
Reputation: 19
Just use the following code.
catch(Exception ex)
{
Exception currentEx = ex;
while (currentEx.InnerException != null)
{
currentEx = currentEx.InnerException;
}
return currentEx;
}
Upvotes: 1
Reputation: 2477
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.
Upvotes: 37
Reputation: 7792
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());
}
Upvotes: 6
Reputation: 1166
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);
}
Upvotes: 31
Reputation: 592
I'm just going to leave the most concise version here:
public static class ExceptionExtensions
{
public static string GetMessageWithInner(this Exception ex) =>
string.Join($";{ Environment.NewLine }caused by: ",
GetInnerExceptions(ex).Select(e => $"'{ e.Message }'"));
public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
{
while (ex != null)
{
yield return ex;
ex = ex.InnerException;
}
}
}
Upvotes: 3
Reputation: 51
public static string GetExceptionMessage(Exception ex)
{
if (ex.InnerException == null)
{
return string.Concat(ex.Message, System.Environment.NewLine, ex.StackTrace);
}
else
{
// Retira a última mensagem da pilha que já foi retornada na recursividade anterior
// (senão a última exceção - que não tem InnerException - vai cair no último else, retornando a mesma mensagem já retornada na passagem anterior)
if (ex.InnerException.InnerException == null)
return ex.InnerException.Message;
else
return string.Concat(string.Concat(ex.InnerException.Message, System.Environment.NewLine, ex.StackTrace), System.Environment.NewLine, GetExceptionMessage(ex.InnerException));
}
}
Upvotes: 5
Reputation: 31484
You mean something like this?
public static class Extensions
{
public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
{
if (ex == null)
{
throw new ArgumentNullException("ex");
}
var innerException = ex;
do
{
yield return innerException;
innerException = innerException.InnerException;
}
while (innerException != null);
}
}
This way you could LINQ over your entire exceptions hierarchy, like this:
exception.GetInnerExceptions().Where(e => e.Message == "Oops!");
Upvotes: 102
Reputation: 59
To add to others, you may want to let the user decide on how to separate the messages:
public static string GetAllMessages(this Exception ex, string separator = "\r\nInnerException: ")
{
if (ex.InnerException == null)
return ex.Message;
return ex.Message + separator + GetAllMessages(ex.InnerException, separator);
}
Upvotes: 5
Reputation: 134611
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);
}
Upvotes: 107
Reputation: 12884
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;
}
}
}
Upvotes: 2
Reputation: 16423
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 + ","));
Upvotes: 8
Reputation: 49310
LINQ is generally used to work with collections of objects. However, arguably, in your case there is no collection of objects (but a graph). So even though some LINQ code might be possible, IMHO it would be rather convoluted or artificial.
On the other hand, your example looks like a prime example where extension methods are actually reasonable. Not to speak of issues like reuse, encapsulation, etc.
I would stay with an extension method, although I might have implemented it that way:
public static string GetAllMessages(this Exception ex)
{
if (ex == null)
throw new ArgumentNullException("ex");
StringBuilder sb = new StringBuilder();
while (ex != null)
{
if (!string.IsNullOrEmpty(ex.Message))
{
if (sb.Length > 0)
sb.Append(" ");
sb.Append(ex.Message);
}
ex = ex.InnerException;
}
return sb.ToString();
}
But that is largely an issue of taste.
Upvotes: 11