Click here to Skip to main content
15,890,557 members
Articles / .NET
Tip/Trick

IDictionary.AddWithType Extension Method

Rate me:
Please Sign up or sign in to vote.
4.69/5 (4 votes)
6 Apr 2016CPOL2 min read 11.3K   3   4
An Extension Method to avoid an undocumented limitation of Exceptions

Background

Like many developers, when I write code that catches Exceptions, I often use the Data property to add context and rethrow it to be handled at a higher level. For example in a library routine that uses ADO.net, I might have something like:

C#
catch ( System.Exception err )
{
    err.Data [ "CommandText" ] = cmd.CommandText ;
    throw ;
}

This has been working as expected, but recently I had an application blow up and log a message like:

System.ArgumentException: Argument passed in is not serializable.
Parameter name: value
   at System.Collections.ListDictionaryInternal.set_Item(Object key, Object value)

I scratched my head for a while, knowing that the code in question doesn't use Dictionaries directly, but does use Exception.Data . So I experimented. Try this:

C#
System.Exception whoops = new System.Exception ( "Whoops" ) ;
whoops.Data [ "Panel" ] = new System.Windows.Forms.Panel() ;

This goes against the documentation, which clearly states "the value component of the pair can be any type of object" .
https://msdn.microsoft.com/en-us/library/system.exception.data(v=vs.110).aspx

Solution

My first thought was to write a custom class that I could use to wrap something before adding it to the Data collection, but because the value would really only be used for logging, the class wouldn't need to do much except have a ToString. So a custom class seemed like overkill. Then I thought about System.Tuple -- is it Serializable? Yes, it is! Try this:

C#
System.Exception whoops = new System.Exception ( "Whoops" ) ;
whoops.Data [ "Panel" ] = new System.Tuple<System.Object> ( new System.Windows.Forms.Panel() ) ;

And what does the System.Tuple.ToString do? It simply makes a string of its contents -- which is exactly what I need for logging.

Using the code

My solution uses a System.Tuple<System.Type,System.Object> so I get the type of the value as well as the value, because I like to log the type, particularly for ADO.net Command Parameters.

I implemented my solution as an Extension Method on IDictionary:

C#
public static System.Tuple<System.Type,System.Object>
AddWithType
(
  this System.Collections.IDictionary Dictionary
,
  System.Object                       Key
,
  System.Object                       Value
)
{
  System.Tuple<System.Type,System.Object> result ;

  if ( Dictionary == null )
  {
    throw ( new System.ArgumentNullException ( "Dictionary" , "You must provide a Dictionary" ) ) ;
  }

  if ( Key == null )
  {
    throw ( new System.ArgumentNullException ( "Key" , "You must provide a Key" ) ) ;
  }

  if ( Value == null )
  {
    result = new System.Tuple<System.Type,System.Object> ( null , "<null>" ) ;
  }
  else
  {
    result = new System.Tuple<System.Type,System.Object> ( Value.GetType() , Value ) ;
  }

  Dictionary [ Key ] = result ;

  return ( result ) ;
}

The method can then be used like this:

C#
catch ( System.Exception err )
{
    err.Data.AddWithType ( "CommandText" , cmd.CommandText ) ;
    throw ;
}

What you do with the Tuple once you catch the Exception higher up the application is beyond the scope of this Tip.

In closing

If you write code that indiscriminately sticks values in the Data collections of Exceptions, you may need to ensure that you are protected against non-serializable objects. Simply wrapping the object in a Tuple is probably enough in most cases.

In case you're wondering, the object that caused the problem was a custom Exception from a third-party API I'm using. In some specific cases, involving the retrying of operations after catching an Exception, I store the Exception that caused the retry in the Data collection of another Exception (no, this is not a case where it should be the InnerException).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
BSCS 1992 Wentworth Institute of Technology

Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.

OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB, acknowledged pedant and contrarian

---------------

"I would be looking for better tekkies, too. Yours are broken." -- Paul Pedant

"Using fewer technologies is better than using more." -- Rico Mariani

"Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’" -- Steve McConnell

"Every time you write a comment, you should grimace and feel the failure of your ability of expression." -- Unknown

"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]

"Typing is no substitute for thinking." -- R.W. Hamming

"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup

ZagNut’s Law: Arrogance is inversely proportional to ability.

"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon

"linq'ish" sounds like "inept" in German -- Andreas Gieriet

"Things would be different if I ran the zoo." -- Dr. Seuss

"Wrong is evil, and it must be defeated." –- Jeff Ello

"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw

“It’s always easier to do it the hard way.” -- Blackhart

“If Unix wasn’t so bad that you can’t give it away, Bill Gates would never have succeeded in selling Windows.” -- Blackhart

"Use vertical and horizontal whitespace generously. Generally, all binary operators except '.' and '->' should be separated from their operands by blanks."

"Omit needless local variables." -- Strunk... had he taught programming

Comments and Discussions

 
QuestionRefinement Pin
Jeff Bowman11-Apr-16 14:47
professionalJeff Bowman11-Apr-16 14:47 
GeneralRe: Refinement Pin
PIEBALDconsult11-Apr-16 14:57
mvePIEBALDconsult11-Apr-16 14:57 
GeneralRe: Refinement Pin
Jeff Bowman11-Apr-16 15:22
professionalJeff Bowman11-Apr-16 15:22 
GeneralSigh... Pin
Brisingr Aerowing6-Apr-16 17:55
professionalBrisingr Aerowing6-Apr-16 17:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.