How can I add a message to an exception without losing any information in C#?

I have the following code:

catch(Exception ex)
{
throw new FatalException("An error occurred while trying to load the XSLT file.", ex);
}

This unfortunately just swallows up the Exception. I can fix this by doing the following:

catch(Exception ex)
{
throw;
}

But I would still like to include the custom message for help with event logging.

How do I add this message to the exception without losing any information? (stack trace/debug symbols, etc.)

39727 次浏览

That original Exception is still there.

When you do your Exception logging, the Exception that you receive will be the FatalException that you made with your message. The original Exception is in ex.InnerException. You can continue to cycle through InnerException until it's null to get all of the Stack Trace information, etc.

In short, don't.

I'm sure you could find some way of getting around this with some reflection, but I would strongly caution you against this. It goes against the original design of exceptions in .NET. Exceptions are not just there to help with logging, they provide information about the original cause of an application failure.

Using the first option is generally preferred as it maintains the stack trace of the original exception but allows you to provide additional information by wrapping it in a separate exception. In my own code, whenever I log exceptions, my logging function will recurse through the InnerException property to find every bit of useful information possible about the error.

If you just need to add information to the original exception, such as a user-readable message or specific details that will be useful to you in tracking down the error but that won't be useful to the end user, you can make use of the Exception's Data property, which is a key/value pair dictionary.

We use this extensively in order to record information such as the report being executed or file that is being processed so that operations can determine what exactly was happening at the time of the error. The user doesn't need this detail since they are working directly with the cause of the failure.

You could also use this to pass a plain text message that makes sense to the user. The only issue is that you will have to perform some additional work in your logging framework or end-user interface in order to extract the data and make it useful to the consumer.

For example, you could do:

catch (Exception ex)
{
ex.Data.Add("UserMessage", "An error occurred while trying to load the XSLT file.");
throw;
}

Then in the client-side code, you could test to see if UserMessage exists and, if so, present it to the user instead of the Exception:

catch (Exception ex)
{
if (ex.Data.Contains("UserMessage"))
{
MessageBox.Show(ex.Data["UserMessage"].ToString());
}
else
{
MessageBox.Show(ex.Message);
}
}

Just in case someone needs a good answer. The key is to use AppDomain.CurrentDomain.FirstChanceException

The you can create a custom object with IDisposable to put all info in it. And if exception happens then FirstChanceException handler gets that info and populate Exception.Data.

Use Local Thread Storage to make it thread safe. Then down the line the code that catches it will get the data and log it.

Example:

using(MyCustomMessage.EnterToLocalStorage("Info for logging"") )
{
...code
...exception thrown
.... FirstChanceException examines local thread storage and get's "info for logging" and puts into Exception.Data.
}
//Dispose is called and all messages that were put into LocalStorage are removed.
//So if exception was not thrown before then it like nothing happened.

Google AsyncDiagnosticStack for a good example. https://github.com/StephenCleary/AsyncDiagnostics/blob/master/src/Nito.AsyncEx.AsyncDiagnostics/AsyncDiagnosticStack.cs