Getting all messages from InnerException(s)?

前端 未结 12 605
闹比i
闹比i 2020-12-02 07:13

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

相关标签:
12条回答
  • 2020-12-02 08:06

    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);
    }
    
    0 讨论(0)
  • 2020-12-02 08:07

    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 + ","));
    
    0 讨论(0)
  • 2020-12-02 08:08

    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);
    }
    
    0 讨论(0)
  • 2020-12-02 08:12

    Most solutions presended here have the following implementation errors:

    • handle null exceptions
    • handle the inner exceptions of AggregateException
    • define a max depth for recurse inner exceptions (ie. with circular dependencies)

    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());
    }
    
    0 讨论(0)
  • 2020-12-02 08:13
    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 08:17

    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.

    0 讨论(0)
提交回复
热议问题