I have a simple connection manager service in my web app that keeps track of websocket connections against a user GUID they are associated with. These are stored in a dictionary with the key being a user GUID and the value being a list of connection IDs (both strings).
ConnectionsByUser = new ConcurrentDictionary<string, List<string>>();
I have a register connection function as below:
public void RegisterConnection(string userGuid, string connectionId)
{
if (connectionId == null)
{
logger.LogError($"Null connection id found for user {userGuid}");
throw new ArgumentNullException(nameof(connectionId));
}
ConnectionsByUser.AddOrUpdate(userGuid, new List<string>() { connectionId }, (key, value) =>
{
value.Add(connectionId);
return value;
});
}
I also have an unregister function:
public void UnregisterConnection(string connectionId)
{
foreach (var user in ConnectionsByUser)
{
if (user.Value.Contains(connectionId))
{
user.Value.Remove(connectionId);
}
if (!user.Value.Any())
{
ConnectionsByUser.TryRemove(user.Key, out var _);
}
}
}
Finally I have a Get function:
public List<string> GetConnectionIdsByUserGuid(string userGuid)
{
var hasValue = ConnectionsByUser.TryGetValue(userGuid, out var connectionIds);
if (!hasValue)
{
return new List<string>();
}
return connectionIds;
}
These are the only references to the ConnectionsByUser
dictionary, yet somehow we have instances where GetConnectionIdsByUserGuid
returns a list containing a null value mixed in with other actual valid connection IDs.
Considering that register function has a null check, I am clueless to how a null value gets into the list, at this point I assume that there is something about the way concurrent dictionary is implemented that I do not understand.