Existe-t-il un moyen d'écrire un code "abrégé" de style LINQ pour accéder à tous les niveaux d'exception (s) d'exception InnerException? Je préférerais l'écrire en place au lieu d'appeler une fonction d'extension (comme ci-dessous) ou d'hériter de la classe 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;
}
};
Malheureusement, LINQ ne propose pas de méthodes qui pourraient traiter des structures hiérarchiques, seulement des collections.
J'ai en fait quelques méthodes d'extension qui pourraient aider à le faire. Je n'ai pas le code exact en main mais ils ressemblent à ceci:
// 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);
}
Ensuite, dans ce cas, vous pouvez le faire pour énumérer les 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);
}
Tu veux dire quelque chose comme ca?
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 cette façon, vous pouvez LINQ sur l'ensemble de votre hiérarchie d'exceptions, comme ceci:
exception.GetInnerExceptions().Where(e => e.Message == "Oops!");
Que diriez-vous de ce code:
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;
}
Usage:
Console.WriteLine(e.GetExceptionMessages())
Exemple de sortie:
Il n'y avait aucun point de terminaison à l'écoute à http: //nnn.mmm.kkk.ppp: 8000/routingservice/router qui pouvait accepter le message. Cela est souvent dû à une adresse incorrecte ou à l'action SOAP. Voir InnerException, le cas échéant, pour plus de détails.
InnerException: impossible de se connecter au serveur distant
InnerException: aucune connexion n'a pu être établie car la machine cible l'a activement refusée 127.0.0.1:8000
Je sais que c'est évident, mais peut-être pas pour tous.
exc.ToString();
Cela passera par toutes vos exceptions internes et retournera tous les messages, mais avec la trace de la pile, etc.
LINQ est généralement utilisé pour travailler avec des collections d'objets. Cependant, sans doute, dans votre cas, il n'y a pas de collection d'objets (mais un graphique). Ainsi, même si un certain code LINQ pourrait être possible, à mon humble avis, il serait plutôt alambiqué ou artificiel.
D'un autre côté, votre exemple ressemble à un excellent exemple où les méthodes d'extension sont réellement raisonnables. Sans parler de problèmes comme la réutilisation, l'encapsulation, etc.
Je resterais avec une méthode d'extension, bien que j'aurais pu l'implémenter de cette façon:
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();
}
Mais c'est en grande partie une question de goût.
Vous n'avez pas besoin de méthodes d'extension ni d'appels récursifs:
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);
}
Pour ajouter à d'autres, vous pouvez laisser l'utilisateur décider de la façon de séparer les messages:
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);
}
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));
}
}
Je ne pense pas, l'exception n'est pas un IEnumerable, vous ne pouvez donc pas exécuter une requête linq contre une seule.
Une méthode d'extension pour renvoyer les exceptions internes fonctionnerait comme ceci
public static class ExceptionExtensions
{
public static IEnumerable<Exception> InnerExceptions(this Exception exception)
{
Exception ex = exception;
while (ex != null)
{
yield return ex;
ex = ex.InnerException;
}
}
}
vous pouvez ensuite ajouter tous les messages à l'aide d'une requête linq comme celle-ci:
var allMessageText = string.Concat(exception.InnerExceptions().Select(e => e.Message + ","));
Je vais juste laisser la version la plus concise ici:
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;
}
}
}
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;
}
}
}