InnerException (s)에서 모든 메시지를 받고 있습니까?


92

던져진 Exception의 모든 수준의 InnerException (s)으로 이동하기위한 LINQ 스타일 "짧은 손"코드를 작성하는 방법이 있습니까? 확장 함수 (아래 참조)를 호출하거나 Exception클래스를 상속하는 대신 제자리에 작성하는 것을 선호합니다 .

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;
    }
}; 

2
확장 방법 이외의 다른 것을 사용하려는 이유를 물어봐도 될까요? 귀하의 코드는 나에게 괜찮아 보이며 코드의 모든 곳에서 재사용 할 수 있습니다.
ken2k

@ ken2k : 당신은 메시지를 그는 ... 바로 지금이 길을 구축하고 싶지 않아요하지만
제프 메르 카도

1
@JeffMercado 예,하지만 "확장 방법"개념의 문제점은 무엇입니까?
ken2k

@ ken2k : 솔직히 말씀 드리면 귀하의 질문을 이해하지 못합니다. 코드에 결함이있을 때 "괜찮아 보인다"고 말씀하셨습니다.
Jeff Mercado 2012

1
AggregateExceptions는 조금 다르게 행동 한다는 점을 명심하십시오 . InnerExceptions대신 재산 을 걸어야 합니다. 여기에 편리한 확장 방법을 제공했습니다 : stackoverflow.com/a/52042708/661933 .
nawfal

답변:


92

불행히도 LINQ는 계층 구조를 처리 할 수있는 메서드를 제공하지 않고 컬렉션 만 제공합니다.

나는 실제로 이것을 도울 수있는 몇 가지 확장 방법이 있습니다. 정확한 코드는 없지만 다음과 같습니다.

// 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);
}

그런 다음이 경우 예외를 열거하기 위해 이렇게 할 수 있습니다.

public static string GetaAllMessages(this Exception exception)
{
    var messages = exception.FromHierarchy(ex => ex.InnerException)
        .Select(ex => ex.Message);
    return String.Join(Environment.NewLine, messages);
}

83

이런 뜻인가요?

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);
    }
}

이렇게하면 다음과 같이 전체 예외 계층 구조에서 LINQ를 수행 할 수 있습니다.

exception.GetInnerExceptions().Where(e => e.Message == "Oops!");

2
제안 된 솔루션보다 훨씬 더
Rice

1
@Rice는 제안 된 솔루션이 다중 평면화 시나리오에 대한이 문제의 일반화라는 점에 유의하십시오. 더 복잡하다는 사실이 예상됩니다.
julealgon

31

이 코드는 어떻습니까?

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;
}

용법:

Console.WriteLine(e.GetExceptionMessages())

출력 예 :

http : //nnn.mmm.kkk.ppp : 8000 / routingservice / router 에서 메시지를 수신 할 수있는 엔드 포인트가 없습니다 . 이는 종종 잘못된 주소 또는 SOAP 작업으로 인해 발생합니다. 자세한 내용은 InnerException (있는 경우)을 참조하십시오.

InnerException : 원격 서버에 연결할 수 없습니다.

InnerException : 대상 컴퓨터가 127.0.0.1:8000을 적극적으로 거부했기 때문에 연결할 수 없습니다.


3
StringBuilder여기서 사용하는 것을 정말로 고려해야 합니다. 또한 IMO 확장 메서드는 NullReferenceExceptionnull 참조에서 호출 될 때 throw되어야합니다 .
dstarkowski

27

한 줄을 기다리는 사람들을 위해.

exc.ToString();

이것은 모든 내부 예외를 통과하고 모든 메시지를 반환하며 단점은 스택 추적 등도 포함된다는 것입니다.


3
예, ToString으로 비난받는 모든 전체 스택 추적으로 행복하게 살면 괜찮습니다. 예를 들어 메시지가 사용자에게 전달되는 경우에는 종종 컨텍스트에 적합하지 않습니다. 반면에 Message는 내부 예외 메시지를 제공하지 않습니다 (재귀하는 ToString과 달리). 우리가 가장 자주 원하는 것은 부모 및 내부 예외의 모든 메시지 인 존재하지 않는 FullMessage입니다.
Ricibob 2017-10-05

16

확장 메서드 나 재귀 호출이 필요하지 않습니다.

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);
}

훌륭한! 내가 그것에 대해 생각했으면 좋겠다.
Raul Marquez

11

LINQ는 일반적으로 개체 컬렉션 작업에 사용됩니다. 그러나 귀하의 경우에는 개체 컬렉션이 없습니다 (그래프 만 있음). 따라서 일부 LINQ 코드가 가능하더라도 IMHO는 다소 복잡하거나 인위적입니다.

반면에, 귀하의 예제는 확장 메서드가 실제로 합리적 인 주요 예제처럼 보입니다. 재사용, 캡슐화 등과 같은 문제는 말하지 마십시오.

확장 방법을 그대로 사용했지만 그렇게 구현했을 수도 있습니다.

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();
}

그러나 그것은 주로 맛의 문제입니다.


7

저는 그렇게 생각하지 않습니다. 예외는 IEnumerable이 아니므로 자체적으로 linq 쿼리를 수행 할 수 없습니다.

내부 예외를 반환하는 확장 메서드는 다음과 같이 작동합니다.

public static class ExceptionExtensions
{
    public static IEnumerable<Exception> InnerExceptions(this Exception exception)
    {
        Exception ex = exception;

        while (ex != null)
        {
            yield return ex;
            ex = ex.InnerException;
        }
    }
}

그런 다음 다음과 같은 linq 쿼리를 사용하여 모든 메시지를 추가 할 수 있습니다.

var allMessageText = string.Concat(exception.InnerExceptions().Select(e => e.Message + ","));

6

다른 사람에게 추가하려면 사용자가 메시지를 분리하는 방법을 결정하도록 할 수 있습니다.

    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);
    }

6
    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));
        }
    }

4

여기에 가장 간결한 버전을 남겨 두겠습니다.

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;
        }
    }
}

3
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;
        }
    }
}

2

여기에 제시된 대부분의 솔루션에는 다음과 같은 구현 오류가 있습니다.

  • null예외 처리
  • 내부 예외 처리 AggregateException
  • 재귀 내부 예외에 대한 최대 깊이 정의 (예 : 순환 종속성 포함)

더 나은 구현은 다음과 같습니다.

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;
}

사용 예 :

try
{
    // ...
}
catch(Exception e)
{
    Log.Error(e, e.AggregateMessages());
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.