भीतरी संदेश (ओं) से सभी संदेश प्राप्त करना


92

वहाँ किसी भी तरह से 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: सच कहूँ तो, मैं वास्तव में आपके प्रश्न को नहीं समझता हूँ ... आपने अभी उल्लेख किया है कि कोड "ठीक है" जब यह त्रुटिपूर्ण है।
जेफ मर्काडो

1
बस मन में सावधान रहना AggregateExceptionथोड़ा अलग व्यवहार करते हैं। आपको InnerExceptionsइसके बदले संपत्ति से चलना होगा । यहां एक आसान विस्तार विधि प्रदान की गई है: दोनों मामलों को कवर करने के लिए stackoverflow.com/a/52042708/661933
नवफाल

जवाबों:


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

81

क्या आपका मतलब इस तरह की किसी चीज से है?

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
प्रस्तावित समाधान की तुलना में बहुत क्लीनर
चावल

1
@ कृपया ध्यान दें कि प्रस्तावित समाधान कई समतल परिदृश्यों के लिए इस समस्या का सामान्यीकरण है। तथ्य यह है कि यह अधिक जटिल है।
जुलैलेगॉन

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: //nn.mmm.kkk.ppp: 8000 / routingservice / राऊटर पर कोई समापन बिंदु नहीं सुना गया जो संदेश को स्वीकार कर सके। यह अक्सर गलत पते या SOAP कार्रवाई के कारण होता है। अधिक विवरण के लिए, यदि उपस्थित हो तो इनरएक्सेप्शन देखें।

भीतरी अपवाद: दूरस्थ सर्वर से कनेक्ट करने में असमर्थ

इनर एक्सेप्शन: कोई कनेक्शन नहीं बनाया जा सका क्योंकि टारगेट मशीन ने इसे 127.0.0.1:8000 से सक्रिय रूप से मना कर दिया


3
आपको वास्तव में StringBuilderयहां उपयोग करने पर विचार करना चाहिए । इसके अलावा IMO विस्तार विधि को NullReferenceExceptionजब शून्य संदर्भ पर लागू किया जाना चाहिए ।
dstarkowski

27

मुझे पता है कि यह स्पष्ट है, लेकिन शायद सभी के लिए नहीं।

exc.ToString();

यह आपके सभी आंतरिक अपवादों के माध्यम से जाएगा और सभी संदेशों को लौटाएगा, लेकिन स्टैक ट्रेस आदि के साथ।


3
यदि आपके सभी पूर्ण स्टैक ट्रेस के साथ रहने के लिए खुश हैं तो यह ठीक है। यदि उपयोगकर्ता किसी संदेश पर जा रहा है तो वह अक्सर संदर्भ के अनुरूप नहीं होता है। दूसरी ओर संदेश आंतरिक अपवाद संदेश नहीं देता है (ToString के विपरीत जो पुनरावृत्ति करता है)। हम जो सबसे अधिक बार चाहते हैं, वह है गैर-मौजूद फुलमैसेज जो माता-पिता और आंतरिक अपवादों से सभी संदेश है।
रिकोबोब

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

प्रतिभाशाली! काश मैंने इसके बारे में सोचा होता।
राउल मार्केज़

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

1

यहां दिए गए अधिकांश समाधानों में निम्नलिखित कार्यान्वयन त्रुटियां हैं:

  • 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());
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.