¿Hay alguna forma de escribir un código de "mano corta" estilo LINQ para caminar a todos los niveles de InnerException (s) de Exception lanzadas? Preferiría escribirlo en su lugar en lugar de llamar a una función de extensión (como se muestra a continuación) o heredar la Exception
clase.
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;
}
};
AggregateException
las mentes se comportan un poco diferente. En su lugar, tendrá que atravesar laInnerExceptions
propiedad. Proporcionó un método de extensión útil aquí: stackoverflow.com/a/52042708/661933 para cubrir ambos casos.Respuestas:
Desafortunadamente, LINQ no ofrece métodos que puedan 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); }
Entonces, en este caso, 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); }
fuente
¿Te refieres a 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 manera, podría LINQ en toda su jerarquía de excepciones, así:
exception.GetInnerExceptions().Where(e => e.Message == "Oops!");
fuente
¿Qué tal 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:
Ejemplo de salida:
No había ningún punto final escuchando en http: //nnn.mmm.kkk.ppp: 8000 / routingservice / router que pudiera aceptar el mensaje. A menudo, esto se debe a una dirección incorrecta o una acción SOAP. Consulte InnerException, si está presente, para obtener más detalles.
InnerException: no se puede conectar al servidor remoto
InnerException: No se pudo establecer la conexión porque la máquina de destino la rechazó activamente 127.0.0.1:8000
fuente
StringBuilder
aquí. También el método de extensión de la OMI debe lanzarseNullReferenceException
cuando se invoca en una referencia nula.Sé que esto es obvio, pero tal vez no para todos.
Esto revisará todas sus excepciones internas y devolverá todos los mensajes, pero junto con el seguimiento de la pila, etc.
fuente
No necesita métodos de extensión ni llamadas recursivas:
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); }
fuente
LINQ se usa generalmente para trabajar con colecciones de objetos. Sin embargo, podría decirse que en su caso no hay una 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 bastante complicado o artificial.
Por otro lado, su ejemplo parece un excelente ejemplo en el que los métodos de extensión son realmente razonables. Por no hablar de cuestiones como la reutilización, la encapsulación, etc.
Me quedaría con un método de extensión, aunque podría haberlo implementado de 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 parte una cuestión de gusto.
fuente
No lo creo, la excepción no es un IEnumerable, por lo que no puede realizar una consulta de linq contra una por sí sola.
Un método de extensión para devolver las excepciones internas funcionaría así
public static class ExceptionExtensions { public static IEnumerable<Exception> InnerExceptions(this Exception exception) { Exception ex = exception; while (ex != null) { yield return ex; ex = ex.InnerException; } } }
luego podría agregar todos los mensajes usando una consulta linq como esta:
var allMessageText = string.Concat(exception.InnerExceptions().Select(e => e.Message + ","));
fuente
Para agregar a otros, es posible que desee dejar que el usuario decida cómo 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); }
fuente
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)); } }
fuente
Solo voy a dejar la versión más concisa aquí:
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; } } }
fuente
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; } } }
fuente
La mayoría de las soluciones presentadas aquí tienen los siguientes errores de implementación:
null
excepcionesAggregateException
Una mejor implementación es esta aquí:
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; }
Uso de ejemplo:
try { // ... } catch(Exception e) { Log.Error(e, e.AggregateMessages()); }
fuente