In our .NET Core with SignalR and Angular – Real-Time charts article, we learned how to implement a real-time chart using ASP.NET Core SignalR on the server-side and Angular on the client side. As part of that article, we implemented a SignalR hub that pushes messages to all connected clients. In this article, we are going to learn how to send client-specific messages using SignalR by modifying the same project.
We highly recommend reading the mentioned SignalR article first and trying out the project we created there, if not already done. That will help you understand the concept of ASP.NET Core SignalR and will make it easy to follow along with this article.
Let’s start
Connections, Users, and Groups in SignalR
SignalR allows messages to be sent to a particular client connection, all connections associated with a specific user, as well as to named groups of connections.
Each client connecting to a SignalR hub has a unique connection id. We can retrieve this using the Context.ConnectionId
property of the hub context. Using this, we can send messages just to that particular client:
public async Task BroadcastToConnection(string data, string connectionId) => await Clients.Client(connectionId).SendAsync("broadcasttoclient", data);
By default, SignalR uses the ClaimTypes.NameIdentifier
from the ClaimsPrincipal
associated with the connection as the user identifier. We can send messages to a particular user using this value:
public async Task BroadcastToUser(string data, string userId) => await Clients.User(userId).SendAsync("broadcasttouser", data);
Remember that when we are sending messages to a user, they will be sent to all connections associated with that user and not just any particular connection. However, sending messages to individual users requires our application to authenticate users and set the NameIdentifier
claim in ClaimsPrincipal
. Only then can the connections be mapped to specific users.
A SignalR group is a collection of connections associated with a name. We can send messages to all connections in a group using the group name. Groups are the recommended way to send messages to multiple connections as it is easy to manage the groups in our application based on the application’s logic. A connection can become a member of multiple groups. Connections can be added to or removed from groups via the AddToGroupAsync()
and RemoveFromGroupAsync()
methods respectively:
public async Task AddToGroup(string groupName) => await Groups.AddToGroupAsync(Context.ConnectionId, groupName); public async Task RemoveFromGroup(string groupName) => await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
Then, we can send messages to the group using the group name:
public async Task BroadcastToGroup(string groupName) => await Clients.Group(groupName) .SendAsync("broadcasttogroup", $"{Context.ConnectionId} has joined the group {groupName}.");
Groups are a good choice when we want to implement notifications specific to particular user groups, roles, etc in our application.
Implementing Client-Specific Messages Using SignalR
In the previous section, we discussed how we can send messages to individual connections, users, and groups. Now let’s straight away implement client-specific messages in our SignalR app. We are going to do that by modifying the applications that we created in the linked article.
In that article, we implemented an ASP.NET Core SignalR server that uses a timer to send real-time data to all connected clients. Then, we implemented an Angular chart in the client app which consumes this data. On clicking the chart, we send a message from the client to the server, which in turn pushes a message to all connected clients.
Here, we are going to modify the last step in such a way that once we click the Angular chart on the client app, it additionally sends the connectionId
to the server, which helps the server to identify this client. Then, the SignalR hub can send a message back to just this client.
To implement this, first, let’s modify the ChartHub
class in our server-side project:
public class ChartHub : Hub { public async Task BroadcastChartData(List<ChartModel> data, string connectionId) => await Clients.Client(connectionId).SendAsync("broadcastchartdata", data); public string GetConnectionId() => Context.ConnectionId; }
We change the BroadcastChartData()
method to accept connectionId
as an additional parameter. This way, we can find the client using the connectionId
and send a message just to that client.
Additionally, we add a new GetConnectionId()
method, which returns the connectionId
of the client.
Next, let’s modify our Angular app. We need to change the SignalRService
to get the connectionId
and pass it while sending the message to the server:
export class SignalrService { public data: ChartModel[]; public connectionId: string; public bradcastedData: ChartModel[]; private hubConnection: signalR.HubConnection public startConnection = () => { this.hubConnection = new signalR.HubConnectionBuilder() .withUrl('https://localhost:5001/chart') .build(); this.hubConnection .start() .then(() => console.log('Connection started')) .then(() => this.getConnectionId()) .catch(err => console.log('Error while starting connection: ' + err)) } ... private getConnectionId = () => { this.hubConnection.invoke('getconnectionid') .then((data) => { console.log(data); this.connectionId = data; }); } public broadcastChartData = () => { const data = this.data.map(m => { const temp = { data: m.data, label: m.label } return temp; }); this.hubConnection.invoke('broadcastchartdata', data, this.connectionId) .catch(err => console.error(err)); } ... }
In the startConnection()
method, we call the getConnectionId()
method, which invokes our hub method to return the connectionId
. Once we get this value, we can set it as a property of the class. Later, when we invoke the broadcastchartdata
hub method, we pass the connectionId
so that our SignalR hub can identify the client using it.
That’s it. We have implemented client-specific message sending in SignalR.
Testing
Now it’s time to test the changes that we have made. For that, we need to run both the server application and the client app. To see client-specific behavior in action, let’s run two instances of the client app. Once two client instances are up and running, let’s click the chart on any one instance:
We can see that the SignalR hub sends a message back to just that client instance. Remember that before implementing this change, when we clicked the chart on any client instance, the SignalR hub was used to send a message to all connected client instances.
Now that we have learned how to send messages to specific clients, we can create a lot of cool features using this technique.
Conclusion
We have learned the following topics in this article:
- The concept of connections, users, and groups in SignalR
- How to send messages from SignalR hub to specific connections, users, and groups
- How to modify our SignalR app to send client-specific messages
why I can’t see the BroadcastChartData method when I type _hub. in controller?
That is not how it works. The _hub object has its own properties that you can use to send your data to a client: _hub.Clients.All.SendAsync (and others of course).
hello , how to send notification form asp.net site to all client in local Lan network .
You can see here: https://code-maze.com/netcore-signalr-angular-realtime-charts/ how to send notification from the server to all the clients using SignalR
Looks interesting
Thanks for the tutorial , I got a question tough :
this implementation will only allow broadcasting to connected users , what if I want to send a notification to an offline client and when this client connects he can see the notifications he missed while offline .
what is the best approach in your opinion , should I store offline notifications somewhere and do a GET at the login method , or is there a better way ?
Thank you
Hello Youssef. To be honest, here, I didn’t work with such a use case but your idea makes perfect sense.
i’ve been searching a comprehensive article like this one for almost a week!! this one is the besti,tnx alot code-maze
You are most welcome. Thanks for reading it.
this article is good but not complete.what happens if when timer triggers we want to send messege to specific client, in this scenario , the client doesn’t initiate the connection so there is no connectionid exist, the question is how I can assign a user to a connection.
what I mean appliation has it’s own user manager but how can I find which connetion is belongs to that user
I am not sure that I understand. How do you expect to send SignalR messages to a specific client if the client didn’t start the SignalR connection at all? In our main example https://code-maze.com/netcore-signalr-angular-realtime-charts/, we first initiate the SignalR connection and then hit the action on the WebAPI that starts the timer. So, the SignalR connection is already open. If it fails to open, you can apply automatic reconnect: https://code-maze.com/signalr-automatic-reconnect-option/
Again, I am not sure that I understood your use case.
imagin you have the notification service, notifcations are generated on server for specific user ,and then server wants to send this new notification to the client, so server knows which user it is but how it can find the connectionid?
I can imagine all of that, but again, your connection must be open in order to use SignalR to send your notifications to the client. So, if your server is already aware of the user, which means that the user is authenticated, why not start the signalR connection at the time user gets authenticated, and then just use it to send notifications?
my problem is how ? signalR server can send messege to specific connectionId, how server can map between user and connectionId?
server has all the connections in clients.All but how it can find which of them belongs to specific user if we don’t set it when connection stablished.
Imagin this senario:user A,B,C are connected to server and after a while server decides to send message to client C , but how server can find between all connections which one belongs to client C?
there should be a way on the server side when connection established that I can tell server ,this is connection for user C
and when another connection established from user B then I tell server this connection belongs to user B and now server has this mapping and can recognize between all connections that it has in clients.All which one belongs to clinet B or C
Let me try to explain it this way.
As you can see in this article, each client/user starts the signalR connection separately and that connectionId is generated on the server per user. This means that you have a different connection for each user/client that consumes your API. So, how you want to tie that connectionId to that specific user is a custom logic, maybe you want to add the connectionid to the table next to the user’s credentials and then use it from there or any other way, but once your client (a single user is a single client) has started the SignalR connection at that moment you have the connectionId for that client and you can relate it to the currently logged in user.
Ok , thanks a lot for clarifications
I went to your code repository at https://github.com/CodeMazeBlog/realtime-charts-signalr-angular/tree/signalR-automatic-reconnect
In there, I found that the branch seemed to be the same as the original article linked. When I downloaded the code, I couldn’t see a difference either.
Hello Peter.
I must say I am a bit confused. This article is about sending specific messages using signalR, but your link is pointing to the source code of the other article about implementing automatic reconnect with signalR. Anyhow, all the branches are different, I’ve just checked again.
You have the main branch for the main article SignalR charts and then two more branches, one for the automatic reconnect and one for specific messages. Again, I’ve just checked the SignalR service file from the client project, and that file is different for all three articles, so everything should be ok there.
Oh no, that’s all my fault. I’m sorry. 🙈
Loved it! Good Articles.
Great starting point for anyone.
Many thanks to the author.
Thanks a lot.
Excelente compa!.
Thanks a lot.
Thanks!!!
Thank you too for reading the article.