Search code examples
c#asp.nethashtableargumentexception

"ArgumentException: Item has already been added" for hashtable


I have a strange behaviour in one of my base methods of an ASP.net web service. First of all: this is code which is productive since years, there has not been reported any problems with it so far. But in my unit tests I recognized two times this problem, so I am confused now, if it is only in my development or if it is a real problem.

Here is my code snippet, where the error

System.ArgumentException: Item has already been added. Key in dictionary: '6' Key being added: '6'

occured when _messages.Add is called:

public Message Add(GatewayMessageTypes type, string strMessage, CultureInfo language)
{
    var message = new Message(type, strMessage, language);

    int intCount = _messages.Count + 1;
    if (_messages.ContainsKey(intCount))
    {
        _messages.Remove(intCount);
    }

    _messages.Add(intCount, message);

    return message;
}

_messages is defined as:

public class MessageHandler
{
    private readonly Hashtable _messages = new Hashtable();

And this MessageHandler is used in all my Web Services defined in a base class

public abstract class ServiceBase
{
    public MessageHandler MessageHandler { get; protected set; }

This is the StackTrace:

   at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
   at System.Collections.Hashtable.Add(Object key, Object value)
   at GISGatewayCore.MessageHandler.Add(GatewayMessageTypes strType, String strMessage, CultureInfo language) in GISGateway\GISGatewayCore\MessageHandler.cs:line 69
   at GISGatewayCore.MessageHandler.AddAndLog(GatewayMessageTypes type, String strMessage, CultureInfo language) in GISGateway\GISGatewayCore\MessageHandler.cs:line 81
   at GISGateway.Services.GetClosestFacilityServices.<>c__DisplayClass4.<GetClosestFacilities>b__1(Object index) in GISGateway\GISGateway.Services\GetClosestFacilityServices.cs:line 592
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart(Object obj)

It is right, there are already 6 entries in the collection, and number 6 looks exactly like the new message which should be added. So what is the problem?

  1. Did the remove fail? Was it to slow in execution? But the code is synchronously, there should not be any problem?
  2. Two different entries with the same values? I am quite sure that this could be excluded
  3. Are there two parallel processes using the same code? My unit tests use this part in common, two of my tests uses the same web service endpoint. But none of my objects are static, so how could this happen?

Solution

  • I understand your point, it should run synchronously. However, try this and see if it works:

    public Message Add(GatewayMessageTypes type, string strMessage, CultureInfo language)
    {
      lock(_messages)
      {
        var message = new Message(type, strMessage, language);
    
        int intCount = _messages.Count + 1;
        if (_messages.ContainsKey(intCount))
        {
            _messages.Remove(intCount);
        }
    
        _messages.Add(intCount, message);
    
        return message;
      }
    }
    

    I may being called from two sources on the same "session", like ajax calls on a controller.