Search code examples
c#asp.net-web-apisignalrsignalr-hub

SignalR Hub throws NRE


I have an issue with null reference exception and can't understand why does it happen. Here is my hub

public class UserHub : Hub
    {
        private static List<Connection> Users = new List<Connection>();

        public override async Task OnConnected()
        {
            string id = Context.ConnectionId;
            string userName = Context.User.Identity.Name;

            if (Users == null)
                Users = new List<Connection>();

            if (!Users.ToArray().Any(_ => _.UserName == userName))
                Clients.AllExcept(Context.ConnectionId).onNewUserConnected(id, userName);

            if (!Users.ToArray().Any(x => x.ConnectionId == Context.ConnectionId))
                Users.Add(new Connection { ConnectionId = id, UserName = userName });

            Clients.Caller.onConnected(id, userName, Users.DistinctBy(_ => _.UserName).ToList());

            await base.OnConnected();
        }


        public override async Task OnReconnected()
        {
            string id = Context.ConnectionId;
            string userName = Context.User.Identity.Name;

            if (Users == null)
                Users = new List<Connection>();

            if (!Users.Any(_ => _.UserName == userName))
                Clients.AllExcept(Context.ConnectionId).onNewUserConnected(id, userName);

            if (!Users.Any(x => x.ConnectionId == id))
                Users.Add(new Connection { ConnectionId = id, UserName = userName });

            Clients.Caller.onConnected(id, userName, Users.DistinctBy(_ => _.UserName).ToList());

            await base.OnReconnected();
        }

        public override async Task OnDisconnected(bool stopCalled)
        {
            if (Users == null)
                Users = new List<Connection>();

            var item = Users.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
                Users.Remove(item);


            Clients.All.onUserDisconnected(Context.ConnectionId, item.UserName);

            await base.OnDisconnected(stopCalled);
        }
    }

the issue occures in onConnected method, in this line:

if (!Users.ToArray().Any(_ => _.UserName == userName))

I've tried to add check for null above but it doesn't work. What's wrong? The issue occures in random times.

Stack trace:

at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
   at App.Web.Hubs.UserHub.<OnConnected>d__1.MoveNext() in E:\Company\App\App\Presentation\App.Web\Hubs\UserHub.cs:line 49
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
...

Solution

  • Your code is not thread safe so you can see all sorts of issues when accessing the Users list. Multiple connections can start or close at the same time and they're all accessing the static Users property. Consider using a concurrent data structure or a lock when accessing the list.