6

I want to modify the Message property in Exception with additional info. For example the generated SQL from EF.

But I don't want to lose anything from the original Exception. This will make me lose the stacktrace:

catch (Exception ex)
{
    throw ex;
}

These Exception's are coming from the Data Layer. And I want to throw them so that they can be logged with Elmah.

What are my options?

Quoter
  • 4,236
  • 13
  • 47
  • 69
  • As an aside (which I think @LB2 was alluding to in his/her answer), you should use `throw;` instead of `throw ex;` since `throw ex;` will not even preserve the original stack trace of your error. – A N Apr 20 '17 at 18:09

6 Answers6

4

If you want to add something you can just wrap it in another exception:

catch( Exception ex)
{
   throw new Exception("my new message",ex);
}

and you will be able to access the inside exception with the full stack trace

Clueless
  • 1,190
  • 2
  • 14
  • 30
  • Although this works, it's terrible advice. Not only would you loss the context of what exception was thrown but it encourages the user to catch a general Exception, which is almost always a bad idea – James Feb 27 '14 at 23:56
  • why would you lose the context if you wrap it as an inner exception? and since the question was - how to keep context and create a new message, it answers the question. I totally agree that you should catch specific exceptions (not always) and that you should create custom exception to throw (not always), but even strongly I believe you should answer the question you were asked too... – Clueless Feb 28 '14 at 00:03
  • you would lose the context in the sense that the actual exception is now an inner exception and your new exception is a general exception - so if you wanted to handle this particular exception you would need to catch Exception and then check the inner exception (which is messy). The proper solution is to derive your own exception and throw that instead. Like I said , your answer will work but it's definitely not the best way of handling this particular scenario. – James Feb 28 '14 at 00:33
4

Define your custom exception class, and put the original exception as the inner exception, and throw the wrapped exception:

public class CustomException : Exception
{
    public CustomException()
        : base()
    {
    }

    public CustomException(string message)
        : base(message) 
    { 
    }

    public CustomException(string message, Exception innerException)
        : base(message, innerException)
    { 
    }

//...other constructors with parametrized messages for localization if needed
}

catch (Exception ex)
{
    throw new CustomException("Something went wrong", ex);
}

Of course its name should be changed accordingly. This way you have full control on exceptions used in your domain, and you don't loose any information from the original throw.

It is very helpful, especially in large projects, to have custom exceptions with good class names. They help in diagnosing problems from first sight in many simple situations, without the need of reading into exception's details and debugging. The latter is needed only when some really elaborate problems occurs. Thus, throwing around bare Exception instances wrapped around each other seems a bad practice.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
2

The short answer to your question is, you can't. An exception is effectively a fault report and as such represents an historical event. Historical events by nature are immutable therefore you can't (nor should want to) "modify" them.

Instead what you can do is add some context to the exception by catching it and throwing a new exception. This will allow you to attach additional diagnostic information and also pass in the original exception thus maintaining the entire fault trail.

I won't go into detail on how to write a custom/use exception as I have already written a detailed answer on this subject before. However, as an example, your handling code should end up looking something like

catch (SomeEntityFrameworkException ex)
{
    throw new MyCustomException("Some additional info", ex);
}
InteXX
  • 6,135
  • 6
  • 43
  • 80
James
  • 80,725
  • 18
  • 167
  • 237
1

I would just make my own exception that contains the original exception and any additional information you want, create the new exception in your catch block and throw it

NeilPearson
  • 128
  • 4
0

Sometimes you just need to edit/replace the original exception.

In my case the ArgumentException for the JavaScriptSerializer.Deserialize() method would return the FULL input as part of the error message if it was invalid.

Since you can't edit an exception, you need to recreate the exception with edited inputs and throw that new exception.

You will lose the original stack trace but if the exception is caught close to the source then it should be close enough.

First get the exception type you want to edit.

catch (Exception ex)
{
    ex.GetType(); // This will return the exception type you need to catch
}

And then catch and recreate it.

catch (ArgumentException ex)
{
    // The error handler seems to be willing to dump out the FULL input data when throwing an exception so we truncate the data.
    const int MAX_DATA_IN_MESSAGE = 80;
    if (String.IsNullOrEmpty(data) || data.Length < MAX_DATA_IN_MESSAGE || !ex.Message.Contains(data))
        throw;
    var truncatedData = data.Substring(0, MAX_DATA_IN_MESSAGE - 1) + "…";
    var newMessage = ex.Message.Replace(data, truncatedData);
    var newEx = new ArgumentException("Can't deserialize input, " + newMessage, ex.InnerException) { Source = ex.Source, HelpLink = ex.HelpLink };
    throw newEx;
}
AnthonyVO
  • 3,821
  • 1
  • 36
  • 41
-1

Just do:

catch (Exception ex)
{
    throw;
}

This passes the exception further without re-throwing it, and preserves context.

LB2
  • 4,802
  • 19
  • 35
  • 1
    How will that add additional info to the `ex.Message` property? – Quoter Feb 27 '14 at 23:34
  • Original code sample left it out, so did I. If it is a custom exception, I presume properties that OP wants to modify are read/write. If exception is read-only, then it can't be done. The only thing I answered is how to re-throw exception without loosing stack and other context of what was captured in original. – LB2 Feb 27 '14 at 23:36