
Suppose we want to create a monitoring web application, that provides users with a dashboard able to display a range of information, which are updated over time.
A first approach is to periodically call an API at defined time intervals (polling) to update data on the dashboard. There’s a problem, anyway: if there are no updated data, we are unnecessarily increasing the network traffic with our requests. An alternative can be the long-polling technique: if the server has no available data, it can keep the request alive until something happens, or the preset timeout is reached, rather than send an empty response. If new data are present, the complete response reaches the client. A completely different approach is to reverse roles: the backend contacts the clients, when new data are available (push).
Remember that HTML 5 has standardized WebSocket, a permanent bidirectional connection, that can be configured with a Javascript interface in compatible browsers. Unfortunately, there must be complete support to WebSocket, both on client and server side, to make it available. We need then to provide for alternative systems (fallback), that allows our application to run, no matter what.
Microsoft has published in 2013 an open-source library called SignalR for ASP.NET, which has been rewritten in 2018 for ASP.NET Core. SignalR abstracts from all details related to communication mechanisms, and it chooses the best one among those available. The result is the possibility to write the code, as if we are always in push-mode. With SignalR the server can call a JavaScript method on all its connected clients, or on a specific one.
We create an ASP.NET Core project with a web-api template, deleting the sample controller, that has been produced. With NuGet, we add Microsoft.AspNet.SignalR to the project, in order to create a Hub. The hub is the high-level pipeline able to call the client code, sending messages containing name and parameters of the requested method. Objects sent as parameters will be de-serialized using the appropriate protocol. The client searches in the page code a method corresponding to the name and, if it finds it, it invokes it passing de-serialized data as parameters.
using Microsoft.AspNetCore.SignalR;
namespace SignalR.Hubs
{
public class NotificationHub : Hub { }
}
As you probably know, in ASP.NET Core the management pipeline of a HTTP request can be configured adding some middleware, that intercepts a request, adds a configured functionality and let it proceed to the next middleware. The SignalR middleware must be configured in advance, adding in the method ConfigureServices of the Startup class the extension method services.AddSignalR(). Now we can add the middleware to the pipeline, using the extension method app.UseSignalR() in the Configure method of the Startup class. During this operation, we can pass configuration parameters, including the route of our hub:
app.UseSignalR(route =>
{
route.MapHub<notificationhub>("/notificationHub");
})
An interesting scenario, that allows us to look at another interesting feature in ASP.NET Core, is the hosting of a SignalR Hub in a background worker process context.
Imagine we want to implement the following use case:
- run business logic
- wait some time
- decide if stop or repeat the process.
In ASP.NET Core, we can use the interface IHostedService provided by the framework, to implement the execution of processes in background in a .NET Core application. Methods to be implemented are StartAsync() and StopAsync(). Very simply: StartAsync is invoked to the host startup, while StopAsync is invoked to the host shutdown.
We add then a class DashboardHostedService to the project, that implements IHostedService. We add in the method ConfigureServices of the Startup class the interface registration:
services.AddHostedService<dashboardhostedservice>();
In the class constructor DashboardHostedService, we inject IHubContext to access to the hub added to our application.
In the method StartAsync we set then a timer, that will run the code contained in the method DoWork() every two seconds. This method sends a message with four strings casually generated.
But to whom does it transmit them? In our example, we are sending the message to all connected clients. However, SignalR offers the opportunity to send messages to single users or to users’ groups. In this article, you find details that involve authentication and authorization functionalities in ASP.NET Core. It is interesting to note, that a user could be connected both on desktop and on mobile. Every device would have a separate SignalR connection, but both of them would be associated with the same user.
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using SignalR.Hubs;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SignalR
{
public class DashboardHostedService: IHostedService
{
private Timer _timer;
private readonly IHubContext<notificationhub> _hubContext;
public DashboardHostedService(IHubContext<notificationhub> hubContext)
{
_hubContext = hubContext;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(2));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_hubContext.Clients.All.SendAsync("SendMessage",
new {
val1 = getRandomString(),
val2 = getRandomString(),
val3 = getRandomString(),
val4 = getRandomString()
});
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
}
}
Let’s see how to manage the client part. We create, for example, an Angular application with the command ng new SignalR of the Angular CLI. We install then the package node for SignalR (npm i @aspnet/signalr). We add then a service, which allows us to connect to the hub previously created and to receive messages.
Here a first possible approach, based on the return by the service of an Observable<Message> in the method getMessage(), by the use of a privately declared Subject<Message> (Message is a typescript interface corresponding to the object returned from the backend):
@Injectable({
providedIn: 'root'
})
export class SignalRService {
private message$: Subject<message>;
private connection: signalR.HubConnection;
constructor() {
this.message$ = new Subject<message>();
this.connection = new signalR.HubConnectionBuilder()
.withUrl(environment.hubUrl)
.build();
this.connect();
}
private connect() {
this.connection.start().catch(err => console.log(err));
this.connection.on('SendMessage', (message) => {
this.message$.next(message);
});
}
public getMessage(): Observable<message> {
return this.message$.asObservable();
}
public disconnect() {
this.connection.stop();
}
}
Inside the constructor(), we create a SignalR.HubConnection type object, that will serve to connect to the server. We pass to its our hub URL by the use of the file environment.ts:
this.connection = new signalR.HubConnectionBuilder()
.withUrl(environment.hubUrl)
.build();
The constructor also looks after to invoke the connect() method, that makes the actual connection, logging possible mistakes in the console.
this.connection.start().catch(err => console.log(err));
this.connection.on('SendMessage', (message) => {
this.message$.next(message);
});
A component that want to show messages coming from the backend (as it inject the service in the constructor), should subscribe to the getMessage() method and manage the arrived message. In the case of AppComponent, for example:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnDestroy {
private signalRSubscription: Subscription;
public content: Message;
constructor(private signalrService: SignalRService) {
this.signalRSubscription = this.signalrService.getMessage().subscribe(
(message) => {
this.content = message;
});
}
ngOnDestroy(): void {
this.signalrService.disconnect();
this.signalRSubscription.unsubscribe();
}
}
Using a Subject <Message> allows us to manage in more components at the same time and regardless of the Message returned from the hub (both for subscribe and for unsubscribe), but we must pay attention to a careless use of the Subject. Let’s consider the following getMessage() version:
public getMessage(): Observable<message> {
return this.message$;
}
Now the component is able to emit a Message it too with this simple code:
const produceMessage = this.signalrService.getMessage() as Subject<any>;
produceMessage.next( {val1: 'a'});
This code throws an exception, if the method getMessage() returns the Subject<Message> asObservable!
A second approach, more simple, that we can use in event of a single component is interested in managing messages coming from the backend, is the following:
@Injectable({
providedIn: 'root'
})
export class SignalrService {
connection: signalR.HubConnection;
constructor() {
this.connection = new signalR.HubConnectionBuilder()
.withUrl(environment.hubAddress)
.build();
this.connect();
}
public connect() {
if (this.connection.state === signalR.HubConnectionState.Disconnected) {
this.connection.start().catch(err => console.log(err));
}
}
public getMessage(next) {
this.connection.on('SendMessage', (message) => {
next(message);
});
}
public disconnect() {
this.connection.stop();
}
}
We can simply pass the function callback to the method getMessage, and the function takes as argument the message coming from the backend. In this case, AppComponent can become:
public content: IMessage;
constructor(private signalrService: SignalrService) {
this.signalrService.getMessage(
(message: IMessage) => {
this.content = message;
}
);
}
ngOnDestroy(): void {
this.signalrService.disconnect();
}
Last few code lines, respectively in app.component.html and app.component.css,to give some stylish, and the application is completed
<div style="text-align:center">
<h1>
DASHBOARD
</h1>
</div>
<div class="card-container">
<div class="card">
<div class="container">
<h4><b>Valore 1</b></h4>
<p>{{content.val1}}</p>
</div>
</div>
<div class="card">
<div class="container">
<h4><b>Valore 2</b></h4>
<p>{{content.val2}}</p>
</div>
</div>
<div class="card">
<div class="container">
<h4><b>Valore 3</b></h4>
<p>{{content.val3}}</p>
</div>
</div>
<div class="card">
<div class="container">
<h4><b>Valore 4</b></h4>
<p>{{content.val4}}</p>
</div>
</div>
</div>
.card-container {
display: flex;
flex-wrap: wrap;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 40%;
flex-grow: 1;
margin: 10px;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
We start the backend for first, then the frontend and check the final result:

It looks fine! You can find the code here: https://github.com/AARNOLD87/SignalRWithAngular
See you next!