1

I have a custom exception class derived from AggregateException. It's shown below.

As you can see, one of the things I do is build a custom Message, where I truncate the inner exceptions to keep it short.

The problem is, if I catch this exception somewhere and then log ex.Message to the console, it includes my message, along with the full stack trace of EVERY SINGLE INNER EXCEPTION appended to my message. The thing is, this aggregate exception can sometimes wrap an absolutely insane number of inner exceptions because it deals with a long running big-data job. Application performance is fine, but it is absolutely destroying our logging.

That is, the message ends up coming out as MyMessage + TonsOfExtraCrapGeneratedByDotNet.

WHY is it overriding my message? Isn't the point of specifying a custom message to give me control? I want it to OVERRIDE - not append.

Here is a runnable .NET Fiddle that reproduces the exact issue: https://dotnetfiddle.net/0x4aD6

public class MyDerivedAggregateException : AggregateException
{
    // ... non-relevant code omitted

    public MyDerivedAggregateException (
        string message, 
        IEnumerable<Exception> innerExceptions,
        bool isEarlyTermination
    ) : base(BuildMessage(message, innerExceptions.ToList(), isEarlyTermination), innerExceptions) 
    {
    }
    
    private static string BuildMessage(string message, List<Exception> innerExs)
    {
        var sb = new StringBuilder();
        sb.AppendLine(message);
  
        int numToShow = Math.Min(5, innerExs.Count);
        int numNotShown = innerExs.Count - numToShow;
            
        for (int i = 0; i < numToShow; i++)
            sb.AppendLine($"Exception {i + 1}: {innerExs[i].Message}");

        if (numNotShown > 0)
            sb.AppendLine($"({numNotShown} more exceptions not shown...)");

        return sb.ToString();
    }
}

I even tested it by adding the following ToString() override to my exception, to exactly what .NET thinks the value of the property is:

        public override string ToString()
        {
            File.WriteAllText("C:/Tests/exmessage.txt", Message);
            File.WriteAllText("C:/Tests/basemessage.txt", base.Message);
            File.WriteAllText("C:/Tests/basetostring.txt", base.ToString());

            return "Screw you .NET";
        }

In my test, the contents of ALL 3 FILES were the same - they included all of the extra crap, rather than just the value of what I provided to base() on AggregateException.

Bassinator
  • 1,682
  • 3
  • 23
  • 50
  • Why are you deriving from AggregateException? What's your goal here? Why don't you inherit from Exception? Some context might help. – Julian Apr 07 '23 at 16:11
  • Because I want it to behave like an aggregate exception, but with some custom behavior that I want and additional info - with things like `Flatten()` etc. – Bassinator Apr 07 '23 at 16:15
  • don't have side effects in `ToString()` – Daniel A. White Apr 07 '23 at 16:34
  • @DanielA.White - I don't think you read the question very well. I don't have side effects in my ToString() - the `ToString()` override I showed above is only for debugging purposes, see inspect the values at runtime. – Bassinator Apr 07 '23 at 16:43
  • Here is a .NET fiddle that reproduces the exact issue: https://dotnetfiddle.net/0x4aD6 – Bassinator Apr 07 '23 at 16:45

1 Answers1

3

You can store your message customizations in a field, and return that one by overriding the Message property.

public class MyDerivedAggregateException : AggregateException
{
    private readonly string _message;

    public MyDerivedAggregateException(
        string message,
        IEnumerable<Exception> innerExceptions,
        bool isEarlyTermination
        ) : base(string.Empty, innerExceptions)
    {
        _message = BuildMessage(message, innerExceptions.ToList());
    }

    public override string Message => _message;

    private static string BuildMessage(string message, List<Exception> innerExs)
    {
        var sb = new StringBuilder();
        sb.AppendLine(message);

        // Other message adjustments go here.

        return sb.ToString();
    }
}
Bassinator
  • 1,682
  • 3
  • 23
  • 50
pfx
  • 20,323
  • 43
  • 37
  • 57
  • So I actually tried this and still had the same results as before. It seems like it ignores the override of the message property. – Bassinator Apr 07 '23 at 16:44
  • Here is a .NET fiddle that reproduces the exact issue: https://dotnetfiddle.net/0x4aD6 – Bassinator Apr 07 '23 at 16:45
  • @Bassinator In your Fiddle, you're overriding `ToString()`, My code overrides the `Message` property. – pfx Apr 07 '23 at 16:47
  • So I'm only overriding ToString() in my fiddle to prove that it isn't being called. – Bassinator Apr 07 '23 at 16:48
  • 1
    Hmm... I just ran your suggestion (which is exactly what I did earlier) and it worked in this .NET fiddle, I will try it again in my code and see if I made a mistake earlier: https://dotnetfiddle.net/FRSlvm – Bassinator Apr 07 '23 at 16:53
  • Hmmm... I'm not sure why my earlier implementation didn't work, but I reverted and re-implemented it and now it works. Thanks for the help! – Bassinator Apr 07 '23 at 17:10
  • 1
    @Bassinator You're welcome! Glad to hear you got to results. – pfx Apr 07 '23 at 17:11