2012-02-16 16 views
54

¿Hay alguna manera de escribir un código LINQ de "mano corta" para caminar a todos los niveles de InnerException (s) of Exception arrojado? Preferiría escribirlo en lugar de llamar a una función de extensión (como a continuación) o heredar la clase Exception.Obteniendo todos los mensajes de InnerException (s)?

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

¿Puedo preguntarle por qué desea utilizar algo más que los métodos de extensión? Su código se ve bien para mí, y es reutilizable en cualquier lugar de su código. – ken2k

+0

@ ken2k: Aunque no le gustaría construir los mensajes de la forma en que lo tiene ahora mismo ... –

+1

@JeffMercado Sí, pero ¿cuál es el problema con el concepto de "método de extensiones"? – ken2k

Respuesta

63

Desafortunadamente LINQ no ofrece métodos que podrían procesar estructuras jerárquicas, solo colecciones.

De hecho, tengo algunos métodos de extensión que podrían ayudar a hacer esto. No tengo el código exacto en la mano pero son algo como esto:

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

Luego, en este caso usted puede hacer esto para enumerar las excepciones:

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

¿Quiere decir algo como esto?

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

De esta forma podría LINQ sobre su jerarquía completa excepciones, como esto:

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

no funciona. – derek

+4

@derek diciendo "no funciona". no es muy útil – Tagc

+3

Está funcionando –

8

LINQ se utiliza generalmente para trabajar con colecciones de objetos. Sin embargo, podría decirse que en su caso no hay colección de objetos (sino un gráfico). Entonces, aunque podría ser posible algún código LINQ, en mi humilde opinión sería un poco intrincado o artificial.

Por otro lado, su ejemplo parece un buen ejemplo donde los métodos de extensión son realmente razonables. Por no hablar de temas como la reutilización, encapsulación, etc.

me quedo con un método de extensión, a pesar de que podría haber implementado esa manera:

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

Pero eso es en gran medida una cuestión de gusto.

4

No lo creo, la excepción no es un IEnumerable, por lo que no puede realizar una consulta linq en contra de uno solo.

un método de extensión para devolver las excepciones internas funcionarían como esto

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

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

usted podría entonces anexar todos los mensajes a través de una consulta LINQ como esto:

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

Cómo sobre este código:

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

Uso:

Console.WriteLine(e.GetExceptionMessages()) 

Ejemplo de salida:

No había ningún punto final a escuchar http://nnn.mmm.kkk.ppp:8000/routingservice/router que podría aceptar el mensaje. Esto a menudo es causado por una dirección incorrecta o acción SOAP. Vea InnerException, si está presente, para más detalles.

InnerException: No se puede conectar con el servidor remoto

InnerException: No se ha podido establecer conexión porque el equipo de destino ha denegado activamente dicha 127.0.0.1:8000

+1

Personalmente, soy un gran seguidor de la recursividad. Buen uso. –

+3

Realmente debería considerar usar 'StringBuilder' aquí. Además, el método de extensión IMO debería arrojar 'NullReferenceException' cuando se invoca en referencia nula. – dstarkowski

+0

¡Esto funciona muy bien! Un caso de uso muy apropiado para la programación recursiva. – Shiva

4

Para añadir a los demás, es posible que desee dejar al usuario a decidir sobre la forma de separar los mensajes:

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

sé que esto es obvio, pero quizás no para todos.

exc.ToString(); 

Esto pasará a través de todas sus excepciones internas y devuelve todos los mensajes, pero en conjunto con la pila rastrear etc.

+1

Sí, eso está bien si estás contento de vivir con todo el rastro de la pila completa que se chamusca con ToString. Eso a menudo no se adapta al contexto, por ejemplo, si el mensaje va a un usuario. Por otro lado, el mensaje NO da el mensaje de excepción interna (a diferencia de ToString que sí se repite). Lo que más a menudo queremos es el FullMessage no existente, que es todo el mensaje del padre y las excepciones internas. – Ricibob

Cuestiones relacionadas