SignalR(v2.2.0) OnDisconnected set user offline

2019-02-20 11:33发布

问题:

I am using the following code to add user in group and save user in db against this particular group using the following code.

SERVER:

  public class ChatHub : Hub
{


    public async Task JoinRoom(string user_Id, string room_Id, string user_Name)
    {
        AddLoginUser(room_Id, this.Context.ConnectionId, user_Id);
        await this.Groups.Add(this.Context.ConnectionId, room_Id);
    }


    public void Connect(string user_Id, string room_Id, string user_Name)
    {
        var id = Context.ConnectionId;

        Clients.Caller.onConnected(id, user_Name, GetRoomUser(room_Id), GetRoomMessage(room_Id));

        // send to all in group to update user list
        Clients.OthersInGroup(room_Id).onNewUserConnected(id, user_Name);
    }
  public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
    {

        using (DataContext dc = new DataContext())
        {
            var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
            {
                item.Connected = false;
                dc.SubmitChanges();

                Clients.OthersInGroup(item.RoomID.ToString()).onUserDisconnected(Context.ConnectionId, item.UserMaster.User_Name);
            }

            return base.OnDisconnected(stopCalled);
        }
    }
   }

 private void AddLoginUser(string room_Id, string connection_Id, string user_Id)
    {
        using (DataContext dc = new DataContext())
        {
            var checkUserLogedIn = (from user in dc.LoggedInUsers
                                    where (user.RoomID == Convert.ToInt32(room_Id) && user.UserID == Convert.ToInt32(user_Id))
                                    select user).SingleOrDefault();
            if (checkUserLogedIn == null)
            {
                LoggedInUser objLoggedInUser = new LoggedInUser();
                objLoggedInUser.ConnectionId = connection_Id;
                objLoggedInUser.UserID = Convert.ToInt32(user_Id);
                objLoggedInUser.RoomID = Convert.ToInt32(room_Id);
                objLoggedInUser.Connected = true;
                dc.LoggedInUsers.InsertOnSubmit(objLoggedInUser);
                dc.SubmitChanges();
            }
            else
            {
                if (!checkUserLogedIn.Connected)
                {
                    checkUserLogedIn.Connected = true;
                    dc.SubmitChanges();
                }
            }
        }
    }

Problem:

Suppose i logged-in with userid=1 for roomid=1 and contextid=123asd. If i refresh my window then contextid will change and now if i closing browser tab then following query:

var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);

not find out the user against latest connectionid, because when i had saved user on connect at that time connectionid was different.

How i can set connected status false for particular user on disconnect event.

Thanks in advance.

回答1:

OnConnected you should save all connectionIds (which is mapped with user), connectionId should be unique, not the user. Because a user can have more than one connection to signalr at the same time(New Tabs).

Everytime you should map user and connectionId on Onconnected. Everytime you should just remove that connectionId, not all connectionIds of user on OnDisconnected. You should add connectionId with user if it's not in list(if stop called is not called disconnected can occur even user is not disconnected) on OnReconnected.

You should refactor your code base on this. First, you should remove connectionId. Then, you can check; if there is no record left with this user(which is mapped with that connectionId) on list, you can send message.

Check here

I have changed your code a bit, you can improve this code based on this knowledge. You should call AddLoginUser on OnReconnected also.

     public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
                {
                    using (DataContext dc = new DataContext())
                    {
                        var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
                        if (item != null)
                        {
                            dc.LoggedInUsers.Remove(item);
                            dc.SubmitChanges();
//If there is no other connection left with this user in this room send message.
                               if (!dc.LoggedInUsers.Any(x => x.RoomID==item.RoomID && x.userId==item.UserId)
                                   Clients.OthersInGrouproomId.ToString()).onUserDisconnected(Context.ConnectionId, item.UserMaster.User_Name);
                           }
                        return base.OnDisconnected(stopCalled);
                    }
                }
            }

            private void AddLoginUser(string room_Id, string connection_Id, string user_Id)
            {
                using (DataContext dc = new DataContext())
                {
//Just check connectionId uniqunes. You don't need connected field.
                    var checkUserLogedIn = (from user in dc.LoggedInUsers
                                            where user.ConnectionId == connection_Id
                                            select user).SingleOrDefault();
                    if (checkUserLogedIn == null)
                    {
                        LoggedInUser objLoggedInUser = new LoggedInUser();
                        objLoggedInUser.ConnectionId = connection_Id;
                        objLoggedInUser.UserID = Convert.ToInt32(user_Id);
                        objLoggedInUser.RoomID = Convert.ToInt32(room_Id);
                        dc.LoggedInUsers.InsertOnSubmit(objLoggedInUser);
                        dc.SubmitChanges();
                    }
                }
            }