Search code examples
asp.net-coresignalrsignalr-hub

How to send Message to User id Saved in AspNetUsers Table Signal R


I am trying to send a message. I have tried to connection id

public Task SendMessaageToConnectionID(string ConnectionID,string Message)
{
    return Clients.Clients(ConnectionID).SendAsync("RecieveMessage", Message);
}

it is successfully done Now I am trying this

public Task SendMessageToUser(string userId,string Message)
{
    return Clients.Clients(userId).SendAsync(Message);
}

I am sending the user id of user Saved in AspNetUser Table

How Can I send this to a User ID or is there any other way except connection id to send the message to user?

Solution

  • SignalR won't store the UserId-ConnectionId mappings for us. We need to do that by our own. For example, when some user sets up a connection to the Hub, it should trigger a ReJoinGroup() method.

    In addition, in order to make sure the Groups property works fine, you need also :

    • invoke RemoveFromGroupAsync to remove the old <connectionId, groupName> mapping
    • invoke AddToGroupAsync to add a new <connectionId, groupName> mapping.

    Typically, you might want to store these information in Redis or RDBMS. For testing purpose, I create a demo that stores these mappings in memory for your reference:

    public class MyHub:Hub
    {
        /// a in-memory store that stores the <userId, connectionId> mappings
        private Dictionary<string, string> _userConn = new Dictionary<string,string>();
        private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    
        public override async Task OnConnectedAsync()
        {
            // get the real group Name by userId, 
            //     for testing purpose, I use the userId as the groupName, 
            //         in your scenario, you could use the ChatRoom Id
            var groupName = Context.UserIdentifier;
            await this.ReJoinGroup(groupName);
        }
    
        // whenever a connection is setup, invoke this Hub method to update the store
        public async Task<KeyValuePair<string,string>> ReJoinGroup(string groupName)
        {
            var newConnectionId = Context.ConnectionId;
            var userId = Context.UserIdentifier;
            await this._semaphore.WaitAsync();
            try{
                if(_userConn.TryGetValue(userId, out var oldConnectionId))
                {
                    _userConn[userId]= newConnectionId;
                    // remove the old connectionId from the Group
                    if(!string.IsNullOrEmpty(groupName)){
                        await Groups.RemoveFromGroupAsync(oldConnectionId, groupName);
                        await Groups.AddToGroupAsync(newConnectionId, groupName);
                    }
                } else {
                    _userConn[userId]= newConnectionId;
                    if(!string.IsNullOrEmpty(groupName)){
                        await Groups.AddToGroupAsync(newConnectionId, groupName);
                    }
                }
            } finally{
                this._semaphore.Release();
            }
            return new KeyValuePair<string,string>(userId, newConnectionId); 
        }
    
        /// your SendMessageToUser() method
        public async Task SendMessageToUser(string userId,string Message)
        {
            // get the connectionId of target user
            var userConn = await this.GetUserConnection(userId); 
            if( userConn.Equals(default(KeyValuePair<string,string>))) {
                throw new Exception($"unknown user connection with userId={userId}");
            }
            await Clients.Clients(userConn.Value).SendAsync(Message);
        }
    
    
        /// a private helper that returns a pair of <UserId,ConnectionId>
        private async Task<KeyValuePair<string,string>> GetUserConnection(string userId)
        {
            KeyValuePair<string,string> kvp = default;
            string newConnectionId = default;
            await this._semaphore.WaitAsync();
            try{
                if(this._userConn.TryGetValue(userId, out newConnectionId)){
                    kvp= new KeyValuePair<string, string>(userId, newConnectionId);
                }
            } finally{
                this._semaphore.Release();
            }
            return kvp;
        }
    
    }