Overview
We will be creating a demo application called Real-time Item Stock monitor using .NET5 and visual studio 2022. Application uses Signal-R to communicate between server and client. Server communicates to client from .NET core Hosted Service which calls a live demo API and pushes the stock data to client to display current stock in a chart. Application used Dependency Injection for invoking Hub instances inside Background Service. Chart.js is used for generating the graph.
Sample application: https://naughty-tharp.5-77-32-140.plesk.page/
1. Creating Project
Create a .NET Core MVC project called RealtimeGraphWithHostedService with Framework .NET5.
2. Adding Client Side Signal-R Library to Project
Right click on the project Add -> Client-side Library. Select provider as ukpkg and search for Library @microsoft/signalr. Select @microsoft/signalr and install it to default location wwwroot/lib.
3. Creating Hub - StockDataHub
Each user connects to server hub from browser using Web sockets, Server sent Events or Long Polling depending on the fallback mechanism automatically and is handled by Signal-R. Below diagram shows the connection and each user will have a connectionid per browser tab per Hub. Hub calls connected users as Clients and they can be accessed inside Hub class using Clients.All, Clients.Caller etc. So let’s create a class inside Hub folder called StockDataHub. To convert it to SigalR Hub inherit it from Hub as shown in below image.
4. Configure Signal-R service and Map Hub endpoint
In startup class to service collection add services.AddSignalR() to enable Signal-R and give a name to Hub endpoint endpoints.MapHub<StockDataHub>("/stockdatahub").
If application contains multiple Hubs each Hub needs to be mapped, in this case, we have only one Hub that is StockDataHub.
Please add usingRealtimeGraphWithHostedService.Hubs Namespace to access StockDataHub inside Startup.cs
Please refer below images.
5. Creating a Model
Inside the model folder create a class called ProductModel.
6. Creating a Service to call Remote API to get Stock Data
Inside Services folder add an Interface IStockData.cs and Implement the method Task<ProductModel> GetStockData(int productid) in StockData.cs as shown below.
7. Create a .NET Core background service named StockServiceBackgroundCaller by Inheriting BackgroundService class inside Services Folder
Background or Hosted services in .Net Core start running as soon as application starts. BackgroundService has a method calledExecuteAsync to implement it in derived class for implementing the functionality. In the constructor of StockServiceBackgroundCaller we are getting instance of IStockData and StockDataHub using Dependency injection by registering them in DI container which is shown in next section. Hub instance are received by using IHubContext<T> where T is the Hub class in our case StockDataHub.
IStockData instance is used to call a demo API with a delay of 2000ms by using await Task.Delay(2000);
StockDataHub calls a client Javascript function populatedata by passing a serialized Json ProductModel to the client and we will implement the populatedata js function in next section.
Once this method is execulted await _stockdataHub.Clients.All.SendAsync("populatedata",JsonSerializer.Serialize(product)) all the connected Signal-R client will receive the latest json data inside the client function populatedata.
8. Registering the above dependencies
Background services are registered using services.AddHostedService<T>() as shown in below image in this case it is services.AddHostedService<StockServiceBackgroundCaller>();
services.AddHttpClient(); is used for getting IHttpClientFactory to make api calls.
services.AddSingleton<IStockData, StockData>(); register dependency to resolve StockData.
9. Client Side Signal-R Connection
Include Signal-R reference in Views/Index.html and canvas for drawing Chart.
Add a js file called signalr-chart.js inside wwwroot/js
i) Initialize Signal-R connection:
var connection = new signalR.HubConnectionBuilder().withUrl("/stockdatahub").build();
The above line of of code builds a connection to the MapHub<T>(endpoint) defined in Startup class and it isendpoints.MapHub<StockDataHub> ("/stockdatahub") in our case using builder pattern.
ii) Start and Establish a Connection:
connection.start().then(() => console.log("hubconnected")).catch(() => console.log(error));//establish connection
The above line of code establishes a connection to the hub and assigns a unique connection-id to Client and logs a message for us to see if connection is successful else logs the error.
iii)Receiving data From Server or Listening to Server events:
Server invoke client method by calling Clients.All.SendAsync("populatedata",JsonSerializer.Serialize(product));. So clients need to listen topopulatedata which can be implemented using connection.on method as follows.
connection.on("populatedata", (product) => { // receive call from StockServiceBackgroundCaller
refreshchartdata(JSON.parse(product));
});
Js function refreshchartdata receives the latest JSON Data and chart is updated which is self explanatory.
10. Application View
View Hosted Demo Application