2012-02-16 2 views
54

Есть ли способ написать код «короткой руки» стиля LINQ для перехода на все уровни исключений InnerException (ы) исключения? Я бы предпочел написать его вместо того, чтобы называть функцию расширения (как показано ниже) или наследовать класс Exception.Получение всех сообщений от InnerException (s)?

+2

Могу ли я спросить вас, почему вы хотите использовать что-то другое, чем методы расширения? Ваш код выглядит хорошо для меня, и он многократно используется в вашем коде. – ken2k

+0

@ ken2k: Хотя вы не хотели бы создавать сообщения так, как он есть сейчас ... –

+1

@JeffMercado Да, но в чем проблема с понятием «метод расширений»? – ken2k

ответ

63

К сожалению 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); 
} 
51

Вы имеете в виду что-то вроде этого?

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!"); 
+1

не работает. – derek

+4

@derek Говоря «это не работает». не очень полезно. – Tagc

+3

Он работает –

8

LINQ, как правило, используется для работы с коллекциями объектов. Однако, возможно, в вашем случае нет набора объектов (но графика). Поэтому, хотя некоторый код LINQ может быть возможен, ИМХО будет довольно запутанным или искусственным.

С другой стороны, ваш пример выглядит как пример, где методы расширения действительно разумны. Не говоря уже о таких вопросах, как повторное использование, инкапсуляция и т.д.

Я хотел бы остаться с методом расширения, хотя я мог бы реализовать это таким образом:

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

Но это во многом вопрос вкуса.

4

Я так не думаю, исключение не является 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 + ",")); 
2
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; 
     } 
    } 
} 
23

Как об этом коде:

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

+1

Лично я большой поклонник рекурсии. Хорошее использование. –

+3

Вы должны действительно рассмотреть возможность использования 'StringBuilder' здесь. Кроме того, метод расширения IMO должен вызывать «NullReferenceException» при вызове нулевой ссылки. – dstarkowski

+0

Это работает действительно красиво! Очень подходящий вариант использования для рекурсивного программирования. – Shiva

4

Чтобы добавить к другим, вы можете захотеть, чтобы пользователю решить, о том, как отделить сообщения:

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

Я знаю, что это очевидно, но, возможно, не для всех.

exc.ToString(); 

Это будет пройти через все ваши внутренние исключения и возвращает все сообщения, но вместе с трассировкой стека и т.д.

+1

Yeh thats fine, если вы счастливы жить со всей полной трассировкой стека, которую вызывают с помощью ToString. Это часто не подходит для контекста, например, если сообщение идет к пользователю. С другой стороны, сообщение НЕ дает внутреннее сообщение об исключении (в отличие от ToString, который рекурсирует). То, что мы чаще всего хотим, - это несуществующий FullMessage, который является всем сообщением от родительских и внутренних исключений. – Ricibob

Смежные вопросы