public override Task OnDisconnected(bool stopCalled)
 {
     try
     {
         _log.Debug("ConnectionId={0}", Context.ConnectionId);
         var connection = ConnectionHolder.Get(Context.ConnectionId);
         if (connection == null)
         {
             return(base.OnDisconnected(stopCalled));
         }
         if (connection.UserId != userId)
         {
             throw new UnexpectedUserIdException(userId, connection.UserId);
         }
         Clients.Group(connection.Group).viewerRemoved(connection.Username);
         Groups.Remove(connection.Id, connection.Group);
         ConnectionHolder.Remove(Context.ConnectionId);
         return(base.OnDisconnected(stopCalled));
     }
     catch (Exception ex)
     { // Not logged by ErrorLoggingPipelineModule.
         _log.Error(ex);
         throw;
     }
 }
        // Exceptions in Hub methods are logged by ErrorLoggingPipelineModule:

        public void StartViewingList(long shoppingListId)
        {
            _log.Debug("shoppingListId={0}, ConnectionId={1}", shoppingListId, Context.ConnectionId);
            string shoppingListIdString = shoppingListId.ToString();
            var    user = _userService.Get(userId);

            if (user == null)
            {
                throw new UserNotFoundException(userId);
            }
            var shoppingList = _shoppingListService.Get(shoppingListId);                                             // Attempt to load the ShoppingList to validate that the current user has the appropriate permissions.

            ConnectionHolder.AddOrUpdate(Context.ConnectionId, shoppingListIdString, userId, user.Username);         // Add a HubConnection for the current user.
            Groups.Add(Context.ConnectionId, shoppingListIdString);                                                  // Add the current user to the group for the ShoppingList.
            Clients.Group(shoppingListIdString).viewerAdded(user.Username);                                          // Inform all clients currently viewing this ShoppingList of the new user that is viewing it.
            Clients.Caller.allCurrentViewersReceived(ConnectionHolder.GetAllUsernamesInGroup(shoppingListIdString)); // Send list of all users viewing this ShoppingList to the caller.
        }