views:

256

answers:

5

Given the following stack trace:

MESSAGE: Value cannot be null.Parameter name: key
SOURCE: mscorlib
TARGETSITE: Void ThrowArgumentNullException(System.ExceptionArgument)
STACKTRACE:
   at System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Collections.Generic.Dictionary'2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary'2.get_Item(TKey key)
   at MyCompany.MAF.Agent.ServiceContracts.ConvertUtils.Convert(Dictionary'2 from) in D:\Development\MAF\Agent\MyCompany.MAF.Agent\ServiceContracts\ConvertUtils.cs:line 11

I conclude that somehow the following block of code has retrieved a null from the input Dictionary's Keys collection. However, the input dictionary is an instance of Dictionary<string, string>. The implementation of Dictionary<string, string> makes that condition impossible. Upon adding an item with a null key, an exception is thrown.

  7 internal static KeyValuePair<string, string>[] Convert(IDictionary<string, string> from)
 8 {
 9     List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();
10     foreach (string key in from.Keys)
11         ret.Add(new KeyValuePair<string, string>(key, from[key]));
12     return ret.ToArray();
13 }
+2  A: 

It looks more like that your IDictionary argument has an item with a Key parameter which is null.

The parameter checking for the IDictionary will probably be happening somewhere internally in the framework.

Tony
Dictionary<string, string> can not be populated with an item with a null key
goofballLogic
@goofballogic, true but you'd only find out at runtime if your key string is null and then get an exception...
Tony
No, Dictionary<string, string> throws an exception if something attempts to populate it with a null key. The exception shown is retrieving the value, not adding/inserting it.
goofballLogic
@goofballlogic the argument type is IDictionary not Dictionary and the former _can_ have a null key (there's nothing in the interface prohibiting that). Do you know that the argument being passed is actually a Dictionary?
Rune FS
Yes this is only referenced from one point in the entire code base which is passing in a Dictionary<string, string>
goofballLogic
+1  A: 

This exception happens if the dictionary key is null. As the built-in Dictionary class doesn't allow such keys, you might be using your own IDictionary-compatible class which allows null.

AndiDog
No the passed in dictionary is a Dictionary<string, string>
goofballLogic
+3  A: 

I've had this problem happen frequently because I made the mistake of allowing multiple threads to access the same dictionary. Make sure that this is not the case, because Dictionary is not thread-safe.

(Incidentally, your method can be greatly simplified. Dictionary<K,V> is already an IEnumerable<KeyValuePair<K,V>>. You should be able to just do ToArray on one.

Jacob
I have tried and failed to create a race condition with threads. Even if there was a threading problem, it would never be possible for <code>key</code> to be initialised to null from the Keys collection
goofballLogic
Good point about the ToArray method - this doesn't explain the exception, but I suspect it would prevent it from happening.
goofballLogic
No, you cannot have a null key, but the internal methods used by `Dictionary` can still throw this exception when entering into certain states. I've seen it happen a lot, especially when using unprotected static dictionaries in my ASP.NET applications.
Jacob
@goofballlogic: That's what one would expect but check the link in my answer for a race condition that results in a Queue.Dequeue() returning a null item when the Queue never contained a null. Without thread safety on these non-thread safe objects the behavior becomes very unexpected.
Cory Charlton
Good point. The exception could be due to a clash internal to the dictionary itself caused by multiple threads. Seems unlikely, but it could happen...
goofballLogic
For bonus points... any idea how to unit test for this? I've tried and failed so far.
goofballLogic
+1, If you don't protect it with a lock, threading can destroy the internal structure and create null entries in the buckets. Dictionary is most vulnerable when it re-organizes itself when the number of elements grows enough to require more buckets.
Hans Passant
I tried before and also failed. Made me wonder if it wasn't so much about threading as much as ASP.NET application pooling. But I learned my lesson. Once I placed locks on static member access code or migrated to storing things in an Application object, these issues went away.
Jacob
+3  A: 

Not sure about the null but why aren't you using:

internal static KeyValuePair<string, string>[] Convert(IDictionary<string, string> from)
{
    return from.ToArray();
}

Edit: As far as the null values are concerned. Do you have multiple threads accessing this IDictionary? Corruption is possible if you're not being thread safe. See this post for an example of corruption in the

Queue<T>

class. http://stackoverflow.com/questions/2214446/how-can-i-modify-a-queue-collection-in-a-loop/2214462#2214462

Cory Charlton
Good point - this doesn't explain the exception, but I suspect it would prevent it from happening.
goofballLogic
@goofballlogic: See my update. If the IDictionary is getting corrupt then simplifing the method might not be the solution. You might need to implement some locking around the IDictionary.
Cory Charlton
A: 

Could it possible be that another thread is affecting the dirctionary being passed into Convert?