3

I'm having difficulty accessing the values of the data property of the Exception class.

try
{
    string strReasonCode = String.Empty;
    string strReasonDescription = String.Empty;
    string strDuration = String.Empty;
    string strActive = String.Empty;

    if (ReasonCodeDT.Rows.Count != 1)
    {
        string strPracticeID = PracticeID.ToString();
        string PracticeName = getSingleStringResultFromSQL("SELECT @result = PracticeName FROM Practice WHERE PracticeID = /'" + strPracticeID + "/'"); 
        string RecordCount = ReasonCodeDT.Rows.Count.ToString();
        int UserID;
        Int32.TryParse(au.UserID, out UserID);
        Exception ex = new Exception("Database Returned " + RecordCount + " Records for ReasonCodeID " + ReasonCodeID + " for Practice " + PracticeName + " (" + strPracticeID + ")");
        ex.Data.Add("UI Error Message", "Whoops! Something went wrong there. Please call (555)555-5555" +
            "and notify them something is wrong with the Encounter Reason: " + ReasonCodeID);
        throw ex;
    }
    else
    {
    }
}
catch
{
    pnlError.Visible = true;
    lblErrorMessage.Text = ex.Data["UI Error Message"];
}

This gives me a compiler error message:

Error 117 Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a cast?)

All the references I can find access that information by using foreach loops specifying the type as DictionaryEntry but that isn't necessary/ desirable for this instance:

How do I retrieve a single item from that dictionary?

UPDATE

In reference to @dbc's suggestion of creating a custom exception class, here is a link providing more detail as to why I didn't do so.

https://blogs.msdn.microsoft.com/jaredpar/2008/10/20/custom-exceptions-when-should-you-create-them/

Feel free to comment as to why an exception class would be better than using the data property of the base exception class.

W.Harr
  • 303
  • 1
  • 4
  • 15

1 Answers1

4

Exception.Data is an an untyped IDictionary:

public virtual IDictionary Data { get; }

Therefore the get and set accessors return and take a value of type object, which you must cast or convert to your desired type, e.g.:

public const string ExceptionMessageKey = "UI Error Message";

lblErrorMessage.Text = ex.Data[ExceptionMessageKey]?.ToString();

(Where ?. is the c# 6.0 null conditional operator). Or alternatively:

lblErrorMessage.Text = ex.Data[ExceptionMessageKey] as string;

As an aside, if you are more familiar with the generic IDictionary<TKey, TValue> interface, take note of the following difference. When using an untyped IDictionary and accessing a key that does not exist, the getter documentation states:

Property Value

The element with the specified key, or null if the key does not exist.

In contrast, the IDictionary<TKey, TValue> documentation states that a KeyNotFoundException will be thrown when the key is not found. There is no TryGetGetValue() for IDictionary.

Sample .Net fiddle here.

Update

Also, is the const keyword mandatory or just best practice in this case? Use of const here is purely good practice. Since you are going to getting and setting exception details in your Data dictionary with standardized keys, it's a good idea to centralize the actual values of those keys in one place in your code which can be used by both producers and consumers of your Exception, e.g. a static constants class:

public static class Constants
{
    public const string ExceptionMessageKey = "UI Error Message";
}

Also, why must one declare the string constant before using the data property of the exception class? -- it isn't necessary. I did that only to make the value of ExceptionMessageKey be a public constant.

When I use the null conditional operator with the syntax you outline, I get a compile time error saying "Syntax Error ':' expected". -- then you are probably using a version of c# earlier than 6.0. If so you can use the following older syntax instead:

var value = ex.Data[Constants.ExceptionMessageKey];
string text = value == null ? null : value.ToString();

Finally, as mentioned in Handling and throwing exceptions in .NET it's good practice to throw some more appropriate derived type of Exception. You could choose a common, pre-existing type such as ApplicationException or define your own, in which case the "UI Error Message" could be made a property of the exception rather than an entry in the Data dictionary.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Great, this worked perfectly. I do have a couple of questions though. When I use the null conditional operator with the syntax you outline, I get a compile time error saying "Syntax Error ':' expected". When I use the syntax with the keyword "as" I get no error. Also, why must one declare the string constant before using the data property of the exception class? Why can't one directly insert a new string value as the key? Also, is the "const" keyword mandatory or just best practice in this case? Thanks for your answer, don't feel obliged to answer my subsequent questions. – W.Harr May 04 '18 at 15:02
  • Thanks for the level of detail in your answer. Originally I had a custom exception class but after some advice, got rid of it in favor of the data property of the base Exception class. This is because the error occurring couldn't be resolved by prompting a user to change any input in the catch block and re trying the action and I only needed to add detail to the exception for error logging/ updating the UI. I will post a link in the question containing more specific information. Do you reasons why custom exception classes would be better suited? – W.Harr May 04 '18 at 16:32
  • @W.Harr - *Do you reasons why custom exception classes would be better suited?* - not really, no. The main reasons to use a custom exception are 1) You need a custom exception handler for that specific exception type; 2) For debugging you need to configure Visual Studio to break (or not break) when an exception of that type is thrown. – dbc May 04 '18 at 16:39
  • Good to know. You seem to really know what you're talking about so I figured I'd pick your brain to see if you knew of anything I was unaware of regarding making that decision. Thanks for all your help – W.Harr May 04 '18 at 19:13