September 28, 2020: I have updated the code sample to .NET Core 3.1 and updated the article to reflect the changes as well. Enjoy!
I have been following Dino Esposito’s SignalR series from the last few MSDN Magazine editions. In the May edition of the Magazine, Dino talked about the subtle details of ASP.Net Core SignalR. It is an excellent read, and it covers the topic in a much better and concise manner than I will ever be able to describe.
In his latest article, Dino discussed the various use cases of SignalR. If you have been watching the Azure space closely, then you must have noticed that a new service named Azure SignalR Service joined the Azure family. I thought of implementing one of the use cases that Dino discussed in the articles using the Azure SignalR Service.
Azure SignalR Service
Azure SignalR Service sits between your clients and your backend services so that you don’t have to implement workarounds for scalability, performance, and availability. If you were to maintain SignalR backend yourself, you would soon run into scalability issues when a large number of persistent WebSocket connections are opened with your application. The Azure SignalR Service sits like a giant computer in front of your backend systems which is capable of maintaining a large number of persistent connections open at all times. The following is the architecture of a simple Azure SignalR Service based system.
Another critical aspect of the architecture is that the backend system need not always be a WebAPI or any other Web Application. You can easily build an Azure Function that communicates with the clients asynchronously. Having the flexibility of choosing the backend systems is helpful in scenarios such as progress monitoring, where a backend system can keep updating the clients on the progress made with the requested operation.
Application
The application that I built is based on Dino’s article in the MSDN magazine on applying SignalR to count the number of active users on a web page. I recommend that you visit the link and read about the use case in detail.
I also referred to the steps for application setup from MSDN. I recommend that you go through the documentation to understand the steps in detail.
Code
The code for this application is present in my GitHub repository.
Building The Application
We will start by writing the hub class. Hub is the main point of connection between the server and the client. Clients can invoke functions on the hub, and the hub can invoke functions on the clients. The Hub
base class which should be inherited by all custom hubs defines some necessary infrastructure to ease development effort. Our hub will increment and decrement a counter value when a client connects and disconnects from the hub respectively.
public class UserCount : Hub
{
private static int Count;
public override Task OnConnectedAsync()
{
Count++;
base.OnConnectedAsync();
this.Clients.All.SendAsync("updateCount", Count);
return Task.CompletedTask;
}
public override Task OnDisconnectedAsync(Exception exception)
{
Count--;
base.OnDisconnectedAsync(exception);
this.Clients.All.SendAsync("updateCount", Count);
return Task.CompletedTask;
}
}
Install the Microsoft.Azure.SignalR nuget package in your application. Now, navigate to the Startup
class and add the SignalR service in the ConfigureServices
method using the following code.
services.AddSignalR().AddAzureSignalR();
Next, we need to connect the hub to a path. To do that, add the following code snippet to the Configure
method in the Startup
class.
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<UserCount>("/chat");
});
The code snippets that we added in the Startup
class will direct all calls from the client to the Azure SignalR Service. The SignalR service will then direct the calls to the hub. Finally, you need to add the SignalR service endpoint as application secret by executing the following command in the same directory as your application’s project file.
dotnet user-secrets set Azure:SignalR:ConnectionString "Endpoint=<Your endpoint>;AccessKey=<Your access key>;"
The client code requires the ASP.net Core SignalR JS library. You can read more about the library and how to use it in your application in the documentation. Here is the code that interacts with the hub and updates the count of visitors on the web page.
document.addEventListener("DOMContentLoaded", function () {
function bindConnectionMessage(connection) {
var messageCallback = function (message) {
console.log("message" + message);
if (!message) return;
var userCountSpan = document.getElementById("users");
userCountSpan.innerText = message;
};
connection.on("updateCount", messageCallback);
connection.onclose(onConnectionError);
}
function onConnected(connection) {
console.log("connection started");
}
function onConnectionError(error) {
if (error && error.message) {
console.error(error.message);
}
}
var connection = new signalR.HubConnectionBuilder().withUrl("/chat").build();
bindConnectionMessage(connection);
connection
.start()
.then(function () {
onConnected(connection);
})
.catch(function (error) {
console.error(error.message);
});
});
Output
Launch your application and verify the request path in the network tab in the debugger console of your browser. You will find that the client interacts with the Azure SignalR Service instead of your application directly.
Launch some instances of the application and then close some of them. You will find that the counter value across the instances keeps changing to reflect the number of clients that have an active channel open with the service.
Conclusion
SignalR gives you the ability to add real-time Web functionalities to your applications. The Azure SignalR Service gives you the ability to scale the application backend by providing a fully managed backplane. We saw just one of the implementations of the use cases of the Azure SignalR Service, and you can follow Dino’s series on MSDN magazine to know about other use cases of SignalR.
Did you enjoy reading this article? I can notify you the next time I publish on this blog... ✍