Reflecting database changes to web application in real time using SignalR, .NET6, Angular and MongoDB

Fábio de Paula Silva
5 min readDec 8, 2022

Nowadays we have many ways to do the same, but today let’s understand a bit more about the SignaR.

SingalR: This open-source library will be the main responsible to provide us a real time web functionality to our applications. It means that our server can send data to any connected client as soon as that data is inserted, updated or deleted, in real-time, and it could be done using two-way: server → client and client ← server. Therefore in this article let’s keeping our focus just in one-way: server to client.

What we are trying to create here is being represented in this flow below.

.NET/Angular using SignalR

Looking to the flow you can see that in our article it will be necessary we creating a timer to say to our hub when he should listen for new changes. But, as I said before, in nowadays there are others ways to do that, without a timer for example, that is, reflecting the changes in really real-time.

Creating ASP.NET project

After have been created the asp.net API project, we need to configure some settings to sync the both sides (front-end and back-end). So, open the program.cs file to set the CORS to establish the communication between the parts. In our case the back-end will run on localhost:7285 and the front-end on localhost:4200.

builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseCors("CorsPolicy");
app.UseAuthorization();

So, take a note that, as you are giving a name for the CORS, it will be necessary to call the app.UseCors passing the CORS name as parameter: app.UseCors(“CorsPolicy”). Remember to call the CORS before the app.UseAuthorization().

Now let’s create our Hub class file in our project.

Tip: If you are using DDD as pattern in your project you should create this file in the domain project.

It’s not mandatory and you can create where you like. But, keep in mind to follow the best practices to be easy to do maintenance code in the future and to keep the service layers uncoupled. So, the name of file will be BookingHub and this class will inherit the Hub class from the Microsoft.AspNetCore.SignalR.

    public class BookingHub : Hub
{
}

Remember that in our project we need to use a timer which will be the responsible to control how many time and for how long we should listen the database changes.

    public class TimerControl
{
private Timer? _timer;
private AutoResetEvent? _autoResetEvent;
private Action? _action;
public DateTime TimerStarted { get; set; }
public bool IsTimerStarted { get; set; }
public void ScheduleTimer(Action action, int paramTime)
{
_action = action;
_autoResetEvent = new AutoResetEvent(false);
_timer = new Timer(Execute, _autoResetEvent, 1000, paramTime);
TimerStarted = DateTime.Now;
IsTimerStarted = true;
}
public void Execute(object? stateInfo)
{
_action();
if ((DateTime.Now - TimerStarted).TotalSeconds > 60)
{
IsTimerStarted = false;
_timer.Dispose();
}
}
}

Take a look at the method ScheduleTimer, it will waiting for two parameters where the second one is the responsible to receive an int variable as a millisecond, who is defining the time to call the action. Example: If you pass 5000 milliseconds to the paramTime, it means that the listening will check for new database changes for each 5 seconds. Now, look at to the other method called Execute. In this one we are creating a kind of time remaining of 60 seconds. It means, we will try catch new changes for each 5 seconds over 1 minute. Feel free to change these values to work as you like and as you need for your solution.

Now we need to set the TimerControl into the program.cs file as Singleton:

builder.Services.AddSingleton<TimerControl>();

Now let’s create our Controller with the method responsible to get the data and changes from our database.

[ApiController]
[Route("api/[controller]")]
public class BookingController : ControllerBase
{
private readonly IHubContext<BookingHub> _bookingHub;
private readonly TimerControl _timerControl;
private readonly IBookingService _bookingService;

public BookingController(IHubContext<BookingHub> paramBookingHub, TimerControl paramTimerControl, IBookingService paramBookingService)
{
_bookingHub = paramBookingHub;
_timerControl = paramTimerControl;
_bookingService = paramBookingService;
}
/// <summary>
/// This method is responsible to return the booking information.
/// </summary>
/// <param name="bookingId"></param>
/// <returns>Return a object BookingResponse with all booking properties</returns>
/// <response code="200">BookingResponse</response>
/// <response code="404">The booking with the parameter informed does not exist.</response>
/// <response code="500">Internal error from Server.</response>
[ProducesResponseType(typeof(BookingResponse), 200)]
[HttpGet]
[Route("/booking/{bookingId}")]
public IActionResult Get(string bookingId)
{
if (!_timerControl.IsTimerStarted)
_timerControl.ScheduleTimer(async () => await _bookingHub.Clients.All.SendAsync("SendBookingData", await BookingService.GetBooking(bookingId)), 2000);
return Ok(new { Message = "Synchronized" });
}
}

I am assuming you already have setup your mongo database and also your DbContext configuration is codified. So, it’s the why I am run down to the main code.

That’s all for the .NET side over here, now let’s jump on the Angular code to create the Hub responsible to make communication with the Hub which we just created in the back-end side.

Installing the SignalR in our Angular project

Open your terminal using the VS Code or any IDE or what else that you prefer and let’s install the signalR in our Angular project.

npm install @microsoft/signalr — save

Now, we need to create a service to our singalR, where we should have to start a Hub connection to the back-end side and the listener receiving new changes from there. So, you can creating this file manually or by code: signalr.service.ts

import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr"
@Injectable({
providedIn: 'root'
})
export class SignalrService {
public data!: any;
private hubConnection!: signalR.HubConnection;

public startConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7285/booking')
.build();
this.hubConnection
.start()
.then(() => console.log('Connection started'))
.catch(err => console.log('Error while starting connection: ' + err))
}

public addBookingDataListener = () => {
this.hubConnection.on('sendbookingdata', (data) => {
this.data = data;
console.log("Booking Name: " + data.name + " - Is Available: " + data.isAvailable);
});
}
}

Important: To work correctly, remember that you need to sync the listener using the same method name used in the back-end side “sendbookingdata”.

In the appComponent.ts or in the file where you will use this functionality we need to instance the signalR service, call the Hub connection and the listener. Take a note that in the startHttpRequest() we are calling our endPoint to get the booking data passing the bookingId as parameter.

export class AppComponent implements OnInit {

name!: string
isAvailable!: Boolean

constructor(public signalRService: SignalrService,
private bookingService: BookingService,) { }

ngOnInit(): void {
this.signalRService.startConnection();
this.signalRService.addBookingDataListener();
this.startHttpRequest();
}

private startHttpRequest = () => {
this.bookingService.getBooking("123").subscribe();
}
}

That’s all that we need so far. Then let’s execute the both project .NET API & Angular web application.

To see the sync changes in real-time open the MongoDb database, the web application and inspect the code in your browser. Note that is possible to identify when I’m changing the values directly in my database the changes are reflected in my web application at the same time.

This presented solution is super simple but might be useful for you. If you want to delve deeper into this open source library, visit this link below:

Hope you are doing good! See you till next article.

--

--

Fábio de Paula Silva

Software Engineer at @Riverty - Inspiring someone to be better doing what you love to do most.