Display Live Sports Updates using ASP.NET Core SignalR

You are currently viewing Display Live Sports Updates using ASP.NET Core SignalR

Innovations like Google Live Search and real-time updates from social media apps e.g. Facebook or Twitter have changed the user’s expectations from the traditional apps to real-time apps. People want to see everything happening live and real-time updates to the web and mobile apps become the norm now. There are many technologies that facilitate this real-time communication between the server and the client and for .NET developers, the most popular choice is to use ASP.NET Core SignalR. Surprisingly, most of the online articles use the chat or messaging app example and I was so fed up seeing those same examples everywhere so I decided to write an article with a more interesting and real-world example related to live sports updates. In this tutorial, I will build a live football match centre application where users will be able to see the football match scores updated in real-time as soon as any particular football team will score a goal. 

Display Live Sports Updates using ASP.NET Core SignalR

What is a Real-Time Application (RTA)?

A real-time application is a type of application that performs time-critical operations. Application has to perform certain functions within a time frame (usually seconds or milliseconds) so that users sense them as immediate or current. In the past, the developers used techniques such as HTTP Long Pooling, Server Side Events (SSE) to implement real-time applications and each one of these techniques had its pros and cons. The modern way of developing a real-time communication system is to use the protocol called Web Sockets that enables two-way communication between the server and the clients for sending and receiving messages and is widely supported by all modern browsers.

Following are some of the common examples of real-time applications

  • Videoconference Apps
  • VoIP Apps
  • Online Gaming Apps
  • Chatting or Instant Messaging Apps
  • Collaboration Apps
  • Social Media Apps
  • GPS Tracking Apps
  • Voting Apps
  • Auction Apps
  • Alerts or Notification Apps
  • Dashboards and Monitoring Apps
  • Stock Ticker Apps

Overview of ASP.NET Core SignalR?

ASP.NET Core SignalR is an open-source library for adding real-time functionality in ASP.NET web applications. It allows us to send server-side push notifications and contents to the client instantly. It provides an API to facilitate server-to-client remote procedure calls (RPC) and using this API, we can call JavaScript functions on clients from the server-side .NET code.

ASP.NET Core SignalR

Following are some of the common features of ASP.NET Core SignalR.

  1. Handles connection management automatically
  2. Sends messages to all connected clients simultaneously. For example, live match updates
  3. Sends messages to specific clients or groups. For example, a specific chat room or a specific user
  4. Scales to handle increasing traffic

To handle real-time communication in a wide range of browsers and devices, SignalR supports the following transport methods and it automatically chooses the best transport method that is within the capabilities of the server and client.

  • WebSockets
  • Server-Sent Events
  • Long Polling

The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection.

Web Sockets enables two-way communication between the server and the clients

SignalR for ASP.NET Core supports any server platform that ASP.NET Core supports and the following table shows the browser support for SignalR.

BrowserVersion
Apple Safari, including iOSLatest Version
Google Chrome, including AndroidLatest Version
Microsoft EdgeLatest Version
Mozilla FirefoxLatest Version

What is a SignalR Hub?

The SignalR Hub is a high-level API that enables us to call methods on connected clients from the server. In the server code, we define methods that are called by the client. In the client code, we define methods that are called from the server. SignalR takes care of everything behind the scenes that makes real-time client-to-server and server-to-client communications possible.

Hubs call client-side code by sending messages that contain the name and parameters of the client-side method. Objects sent as method parameters are deserialized using the configured protocol. The client tries to match the name to a method in the client-side code. When the client finds a match, it calls the method and passes to it the deserialized parameter data.

To create a hub in your code, you need to create a class that inherits from the Hub class and then you can create public methods in the Hub. These public methods can be called from connected clients. In the example below when any connected client will call the SendMessage method then the method will broadcast the user and message parameters values to all the clients connected to this Hub.

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

If we don’t want to broadcast messages to all connected clients, then we also have other options. The following table shows the available methods.

MethodDescription
AllCalls a method on all connected clients
CallerCalls a method on the client that invoked the hub method
OthersCalls a method on all connected clients except the client that invoked the method
AllExceptCalls a method on all connected clients except for the specified connections
ClientCalls a method on a specific connected client
ClientsCalls a method on specific connected clients
GroupCalls a method on all connections in the specified group
GroupExceptCalls a method on all connections in the specified group, except the specified connections
GroupsCalls a method on multiple groups of connections
OthersInGroupCalls a method on a group of connections, excluding the client that invoked the hub method
UserCalls a method on all connections associated with a specific user
UsersCalls a method on all connections associated with the specified users

Overview of Football Match Centre Application

I covered some of the basic concepts of ASP.NET Core SignalR. It is now time to build a real-world application that will cover everything we have learned so far. The application will consist of the following two pages

Admin Page – This page will display the list of football matches and will allow the admin to update football match scores. Whenever any team will score a goal, an admin will click the Add Goal button to add one goal for any particular team.

READ ALSO:  Observer Design Pattern in ASP.NET Core
SignalR - Admin Page with Updated Goals

Match Centre Page – This page will display the list of matches to all live users. Every time, an admin will add a goal to any team from the admin page, the match results will update for all online users in real-time.

SignalR - Match Center Live Page

Create Database and EF Core DBContext

Before we start building the application, I created a database named FootballDb in SQL Server 2016 and the database has the following two tables.

Database Schema for Football Match Center SignalR Application

The Teams table will store minimum team information and the Matches table will store the information about the matches scheduled to be played between two Teams. Both Team1 and Team2 columns in the Matches table will be foreign keys and they will store the primary key value from the Teams table. I also added the following information about some English Premier League football teams and matches.

Teams and Matches Data in Database for SignalR Application

Let’s start by creating an ASP.NET Core MVC Web Application in Visual Studio 2019. We need to connect the database to display match information and for this tutorial, I will use Entity Framework Core (Database First) approach. If you don’t know how to use Entity Framework Core, then you can read my post Data Access in ASP.NET Core using EF Core (Database First). We need to install the following packages to use Entity Framework Core in our application so add these packages using the NuGet package manager.

Add the following connection string in appsettings.json file and don’t forget to replace the DB_SERVER with your database server name in the connection added in asppsettings.json file

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=DB_SERVER; Database=FootballDb; Trusted_Connection=True; MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Next, add a Data folder in the project and run the following command in Package Manager Console to generate the entity models and the DbContext class with the name FootballDbContext.

Scaffold-DbContext -Connection "Server=DB_SERVER; Database=FootballDb; Trusted_Connection=True; MultipleActiveResultSets=true;" -Provider Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Models" -ContextDir "Data" -Context "FootballDbContext" -NoOnConfiguring

Final step is to configure the Entity Framework provider for SQL Server in the ConfigureService method of the Startup.cs file as follows:

services.AddDbContext<FootballDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Implement Application Services and Web APIs

Our application will call Web APIs from both admin and match centre pages and those Web APIs will call the backend application services to communicate with the database. Create a Services folder in the project and create the following IFootballService interface.

IFootballService.cs

public interface IFootballService
{
    Task<IEnumerable<MatchViewModel>> GetMatchesAsync();
    Task UpdateMatchAsync(MatchUpdateModel model);
}

Our application only needs two methods to perform its operations.

  1. GetMatchesAsync method will return the list of matches to be displayed on both pages
  2. UpdateMatchAsync method will update the match information in the database whenever the admin will click the “Add Goal” button on the admin page. 

Both of the methods above are using two View Model classes and you can declare these classes in the Models folder. Here is the definition of MatchViewModel class.

MatchViewModel.cs

public class MatchViewModel
{
    public int Id { get; set; }
    public DateTime? Date { get; set; }

    public int Team1Id { get; set; }
    public int Team2Id { get; set; }

    public string Team1Name { get; set; }
    public string Team2Name { get; set; }

    public string Team1Logo { get; set; }
    public string Team2Logo { get; set; }

    public int? Team1Goals { get; set; }
    public int? Team2Goals { get; set; }
}

Following is the definition of MatchUpdateModel class

MatchUpdateModel.cs

public class MatchUpdateModel
{
    public int? MatchId { get; set; }
    public int? TeamId { get; set; }
}

Next, create the following FootballService class in the Services folder and implement the IFootballService interface on the class. We need to define both methods declared in the IFootballService interface. Notice, how the FootballDbContext is injected into the service constructor to make sure we can connect the FootballDb database and perform database operations.

FootballService.cs

public class FootballService : IFootballService
{
    private readonly FootballDbContext _context;

    public FootballService(FootballDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<MatchViewModel>> GetMatchesAsync()
    {
        var query = from m in _context.Matches
            join t1 in _context.Teams on m.Team1 equals t1.Id
            join t2 in _context.Teams on m.Team2 equals t2.Id
            select new MatchViewModel()
            {
                Id = m.Id,
                Team1Id = t1.Id,
                Team2Id = t2.Id,
                Team1Name = t1.Name,
                Team2Name = t2.Name,
                Team1Logo = t1.Logo,
                Team2Logo = t2.Logo,
                Team1Goals = m.Team1Goals ?? 0,
                Team2Goals = m.Team2Goals ?? 0
            };
        return await query.ToListAsync();
    }

    public async Task UpdateMatchAsync(MatchUpdateModel model)
    {
        var match = _context.Matches.FirstOrDefault(x => x.Id == model.MatchId);
        if (match != null)
        {
            if (model.TeamId == match.Team1)
            {
                match.Team1Goals = (match.Team1Goals ?? 0) + 1;
            }

            if (model.TeamId == match.Team2)
            {
                match.Team2Goals = (match.Team2Goals ?? 0) + 1;
            }

            _context.Matches.Update(match);
            await _context.SaveChangesAsync(); 
        }
    }
}

The GetMatchesAsync method is using LINQ to join both Teams and Matches table using the Team1 and Team2 foreign keys and then returns the list of MatchViewModel class objects. The UpdateMatchAsync method will first query particular match information from the database and then it will add 1 goal in either Team1 or Team2 depending upon the TeamId value passed to it from the front end. If the admin will add a goal in Team 1 then the Team1 goals will be incremented by 1 and if the admin will add a goal in Team 2 then the Team 2 goals will increment by 1.

To inject and use the above FootballService in controllers, we need to register it with ASP.NET Core dependency injection container and you can do this by adding the following line in the ConfigureService method of the Startup.cs file

services.AddScoped<IFootballService, FootballService>();

To create the Web API, add the following MatchController class in the Controllers folder. The class inherits from the ControllerBase class and the IFootballService interface is injected in the class using the constructor. The methods of the class are pretty straightforward as they are simply calling the corresponding methods we defined in the FootballService above.

MatchController.cs

[ApiController]
public class MatchController : ControllerBase
{
    private readonly IFootballService _footballService;  

    public MatchController(IFootballService footballService)
    {
        _footballService = footballService; 
    }

    // GET: api/Matches
    [HttpGet]
    [Route("api/Matches")]
    public async Task<IEnumerable<MatchViewModel>> GetMatchesAsync()
    {
        return await _footballService.GetMatchesAsync();
    }

    // PUT: api/Matches
    [HttpPut]
    [Route("api/Matches")]
    public async Task UpdateMatchAsync(MatchUpdateModel model)
    {
        await _footballService.UpdateMatchAsync(model); 
    }
}

We are defining the API URLs using the [Route] attribute so we have to add MapControllers method call in Startup.cs file.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapControllers(); 
});

To make sure everything is working as expected, run the project by pressing F5 and open the following URL in the browser address bar. Please note that the port number shown in the following URL can be different in your case.

http://localhost:58316/api/Matches

You should be able to see the matches information returned from the Web API in JSON format

Match Web API returning list of Matches

If you want to learn more about ASP.NET Core Web APIs then read my post A Developer’s Guide for Creating Web APIs with ASP.NET Core 5

READ ALSO:  Distributed Caching in ASP.NET Core using Redis Cache

Creating Admin Page to Update Football Match Scores

Once our database, services, and Web APIs are ready, it is now time to develop the Admin Page that will allow admin to update match scores. Create the following AdminController class in the Controllers folder and add an Index action method in it.

AdminController.cs

public class AdminController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

The Index method will display the Index.cshtml Razor View available in Views/Admin folder. Open the Index.cshtml file and add the following code to it.

@{
    ViewData["Title"] = "Admin Area";
}

<table class="table match-center">
    <tbody id="listMatches">
    </tbody>
</table>

@section Scripts{
    <script src="~/js/admin.js"></script>
}

There isn’t too much going on in the view. I declared an HTML table with an empty <tbody> element. The important thing to note is that the <tbody> element has been given an id listMatches. This is because I will fetch the list of matches from the Web API using JQuery AJAX get method and will generate the table rows dynamically. In the end, I included a JavaScript file admin.js which we need to add next in wwwroot/js folder.

admin.js

$(document).ready(function () {
    loadMatches(); 
});

function loadMatches() {
    $.get("/api/Matches", null, function (response) {
        bindMatches(response);
    });
}

function bindMatches(matches) {
    var html = "";
    $("#listMatches").html("");
    $.each(matches,
        function(index, match) {
            html += "<tr data-match-id='" + match.id + "'>";

            html += "<td>";
            html += "<img class='team-logo' src='/images/" + match.team1Logo + "'/>";
            html += "<span class='team-name'>" + match.team1Name + "</span>";
            html += "</td>";

            html += "<td>";
            html += "<span data-team-id='" + match.team1Id + "' class='team-goals'>" + match.team1Goals + "</span>";
            html += "<input type='button' class='btn btn-success' value='Add Goal' data-match-id='" + match.id + "' data-team-id='" + match.team1Id + "' onclick='addGoal(this);' />";
            html += "</td>";

            html += "<td>";
            html += "<span class='team-separator'> — </span>";
            html += "</td>";

            html += "<td>";
            html += "<span data-team-id='" + match.team2Id + "' class='team-goals'>" + match.team2Goals + "</span>";
            html += "<input type='button' class='btn btn-success' value='Add Goal' data-match-id='" + match.id + "' data-team-id='" + match.team2Id + "' onclick='addGoal(this);' />";

            html += "</td>";

            html += "<td>";
            html += "<img class='team-logo' src='/images/" + match.team2Logo + "'/>";
            html += "<span class='team-name'>" + match.team2Name + "</span>";
            html += "</td>";

            html += "</tr>";
        });
    $("#listMatches").append(html);
}

In the admin.js file, I am calling a loadMatches function that will make an AJAX call to our Web API endpoint api/Matches, and once we have the API response available it will simply pass the response to bindMatches function that will generate the HTML table rows with matches information.

If you will run the project now and will navigate to /Admin page, you will see output similar to the following.

SignalR - Admin Page with Updated Goals

Note that I am loading the team logos from the wwwroot/images folder and I have downloaded the team logos from a stock photos website. You will be able to get the logos with the article source code that I will upload to GitHub and will make available for you to download.

Another important thing to notice is that all <input> button elements have some dynamic attributes data-match-id and data-team-id which are set using the match and teams Ids we are receiving in API response. Finally, we have onclick event handler which is bind with the addGoal function.

To complete this page, we need to implement addGoal method. This method will first get the matchId and teamId values from the data-match-id and data-team-id attributes we added to each <input> element and then it will send these parameters to api/Patches API using the HTTP PUT method. If the AJAX call is successful, we will once again call the loadMatches method to refresh the page

function addGoal(element) {
    var data =
    {
        matchId: $(element).attr("data-match-id"),
        teamId: $(element).attr("data-team-id")
    }

    $.ajax({
        type: 'PUT',
        url: 'api/Matches',
        contentType: 'application/json',
        data: JSON.stringify(data)
    }).done(function() {
        loadMatches(); 
    });
}

Press F5 to run the project and try updating some matches information by adding some Goals. If you will open the database, you will also see that the goals information is also updating in the Matches table in the database. We will come back to this page later when we will add SignalR related functionality in our project. It is now time to build the Match Centre page for public users.

Creating Match Centre Page for Public Users

The layout and the look and feel of our Match Centre page are almost the same as the admin page. The only difference is that the Match Centre page doesn’t have the “Add Goal” buttons available. Let’s quickly create the HomeController with the following Index action.

HomeController.cs

public class HomeController : Controller
{ 
    public IActionResult Index()
    {
        return View();
    } 
}

Following is the code for Index.cshtml view and it is also almost the same as the Index.cshtml view of AdminController we created earlier. The only difference is that it is including the home.js file.

Index.cshtml

@{
    ViewData["Title"] = "MATCH CENTER LIVE";
}

<table class="table match-center">
    <tbody id="listMatches">
    </tbody>
</table>

@section Scripts{  
    <script src="~/js/home.js"></script>
}

Following is the code for home.js file which is almost same as admin.js file we created earlier but this time it doesn’t have the Add Goal buttons in it.

home.js

$(document).ready(function () {
    loadMatches(); 
});

function loadMatches() {
    $.get("/api/Matches", null, function (response) {
        bindMatches(response);
    });
}

function bindMatches(matches) {
    var html = "";
    $("#listMatches").html("");
    $.each(matches,
        function(index, match) {
            html += "<tr data-match-id='" + match.id + "'>";

            html += "<td>";
            html += "<img class='team-logo' src='/images/" + match.team1Logo + "'/>";
            html += "<span class='team-name'>" + match.team1Name + "</span>";
            html += "</td>";

            html += "<td>";
            html += "<span data-team-id='" + match.team1Id + "' class='team-goals'>" + match.team1Goals + "</span>";
            html += "</td>";

            html += "<td>";
            html += "<span class='team-separator'> — </span>";
            html += "</td>";

            html += "<td>";
            html += "<span data-team-id='" + match.team2Id + "' class='team-goals'>" + match.team2Goals + "</span>";
            html += "</td>";

            html += "<td>";
            html += "<img class='team-logo' src='/images/" + match.team2Logo + "'/>";
            html += "<span class='team-name'>" + match.team2Name + "</span>";
            html += "</td>";

            html += "</tr>";
        });
    $("#listMatches").append(html);
}

Press F5 to run the project and you will see a page similar to the following

Match-Center-Live-Page

Try to open both pages side by side and then click the Add Goal button for any match and team. You will see that the Match Centre page is not updating. This is because the Match Centre page has no idea that the match information is updated in the database and it needs to fetch the updated information from the server. If you will refresh the Match Centre page, then the match scores will update. We don’t want the user to keep refreshing the page to see live match updates. We want to update the Match Centre page in real-time without any user interaction and this is where we will use SignalR that will allow us to send real-time notifications from server to client so that we can update the pages on the client instantly.

READ ALSO:  Consuming GraphQL APIs in ASP.NET Core
Display Live Sports Updates using ASP.NET Core SignalR

Getting Started with ASP.NET Core SignalR

SignalR supports bi-directional communication between the server and the client which means we need to have SignalR libraries at both ends. The SignalR server library is included in ASP.NET Core shared framework so we don’t need to install any package or library on the server. However, we need to install the client library and the easiest way to add the client library to our project is to use the LibMan tool. Follow the steps below to add the SignalR client library to the project.

  • Right-click on the project in Solution Explorer
  • Choose Add > Client-Side Library menu option.
  • In the Add Client-Side Library dialog, for Provider select unpkg.
  • For Library, enter @microsoft/signalr@latest.
  • Select Choose specific files, expand the dist/browser folder, and select signalr.js and signalr.min.js.
  • Set Target Location to wwwroot/lib/microsoft/signalr/, and select Install.
  • LibMan will create a wwwroot/lib/microsoft/signalr folder and will copy the selected files in it.
Download SignalR JavaScript Library using LibMan

If you want to learn more about managing server and client libraries in ASP.NET Core projects then you can read my post Working with Packages and Libraries in ASP.NET Core

Once the SignalR libraries are available, we need to create SignalR hub class. The hub class is a high-level abstraction and it handles the client-server communication. It manages connections, groups, and messaging and provides an easy-to-use API to facilitate advanced real-time scenarios. Create a Hubs folder in the project and define the following MatchCentreHub class inherited from the Hub class.

public class MatchCentreHub : Hub
{
    public async Task SendMatchCentreUpdate()
    {
        await Clients.All.SendAsync("RefreshMatchCentre");
    }
}

There is just one method SendMatchCentreUpdate in MatchCentreHub class and this method can be called by a connected client to send notifications to other connected clients. I am sending notifications to All connected clients and you can also see the client-side method name RefreshMatchCentre mentioned as a parameter in the SendAsync method. We will define this method in our client-side code later in this tutorial.

The next step is to configure server-side SignalR and we can do this by calling the AddSignalR method in the ConfigureService method of the Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();

    services.AddControllersWithViews();

    services.AddDbContext<FootballDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddScoped<IFootballService, FootballService>();
}

We also need to add SignalR to the routing systems so that we can expose the hub endpoint for the clients who want to connect to the hub and we can do this using the MapHub method shown below.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapControllers();

    endpoints.MapHub<MatchCentreHub>("/matchcenterhub");
});

Working with SignalR JavaScript Client Library

We need to connect Match Centre Page with the MatchCentreHub at the server so that when the MatchCentreHub sends notifications to Match Centre Page and call a client-side function RefreshMatchCentre we can receive the notifications on the client and instantly refresh the Match Centre Page to give users the feeling that the page is updating in real-time.

Include the SignalR JavaScript library in the Index.cshtml page using the standard script element:

@section Scripts{ 
    <script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
    <script src="~/js/home.js"></script>
}

Next, create the connection object in the home.js file by adding the following line at the top of the file. Notice that the connection is built with the same URL we defined at the server using the MapHub configuration above.

var connection = new signalR.HubConnectionBuilder().withUrl("/matchcenterhub").build();

We want Match Centre Page to connect to server side hub as soon as the page is loaded so call the start function as soon as the document is ready.

$(document).ready(function () {
    loadMatches();
    connection.start();
});

You can confirm if the WebSockets connection is established successfully by refreshing the page and seeing the following lines in the browser console window.

SignalR WebSockets connection info in browser console

The last piece of code we need is to define the RefreshMatchCentre function which will be called from the server whenever the server will send notifications to the client. I am just calling the loadMatches function in RefreshMatchCentre to refresh the matches on the page.  

connection.on("RefreshMatchCentre", function (match) {
    loadMatches();
});

Match Centre Page is now complete and ready to receive notifications from the server and as soon as it will receive the notification, it will refresh the page and show the live match updates to the users.

We need to repeat almost same steps for Admin page as well so let’s include the SignalR client library on Index.cshtml view of AdminController.

@section Scripts{
    <script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
    <script src="~/js/admin.js"></script>
}

Build the connection object by adding the following line on top of the admin.js file

var connection = new signalR.HubConnectionBuilder().withUrl("/matchcenterhub").build();

Call the start function as soon as the document is ready

$(document).ready(function () {
    loadMatches();
    connection.start();
});

Next, we need to write the most important line of this application. This line will invoke the method SendMatchCentreUpdate defined in the MatchCentreHub class available on the server. We already know that whenever any client will call the SendMatchCentreUpdate method, the notification will be sent to all connected clients automatically. The best place to invoke the SendMatchCentreUpdate method is when the admin updated the match by clicking the Add Goal button.

function addGoal(element) {
    var data =
    {
        matchId: $(element).attr("data-match-id"),
        teamId: $(element).attr("data-team-id")
    }

    $.ajax({
        type: 'PUT',
        url: 'api/Matches',
        contentType: 'application/json',
        data: JSON.stringify(data)
    }).done(function() {
        loadMatches();

        connection.invoke("SendMatchCentreUpdate").catch(function (err) {
            return console.error(err.toString());
        });
    });
}

It is now time to see the complete application in action. Press F5 and once again open both pages side by side. Try to click the Add Goal button on the Admin page and you will notice that the Match Centre Live Page will update in real-time.

Display Live Sports Updates using ASP.NET Core SignalR

Following is the visual representation of client-server communication happening in this application using SignalR.

Live Sports Updates using ASP.NET Core SignalR

Broadcasting from Server using ASP.NET Core SignalR

In the above example, we were invoking the SendMatchCentreUpdate method of MatchCentreHub class from the client side using the SignalR client library invoke function which we added inside addGoal function.

connection.invoke("SendMatchCentreUpdate").catch(function (err) {
    return console.error(err.toString());
});

Sometimes, our scenario is such that we want to invoke hub methods from server-side .NET code and we want to broadcast messages and push notifications from the server to clients when some server-side event occurred. We can easily achieve this using SignalR server-side APIs. In this section of this tutorial, I will show you how you can broadcast messages from the server to clients using SignalR. Before we start, first remove all the client-side code we added in our admin page above and also remove the line where we included the signalr.js file in our admin page.

We are already calling the server-side Web API method UpdateMatchAsync to update the goals in the database so this method is the ideal candidate to invoke the SendMatchCentreHub method which will automatically send notifications to all connected clients.

public async Task UpdateMatchAsync(MatchUpdateModel model)
{
    await _footballService.UpdateMatchAsync(model);
}

Open the MatchController class declare the following object of IHubContext type.

private readonly IHubContext<MatchCentreHub> _hub;

IHubContext allows us to send messages from different areas of the application and we can inject IHubContext in controllers and services using dependency injection. Inject the IHubContext in MatchController as shown below.

public MatchController(IFootballService footballService, IHubContext<MatchCentreHub> hub)
{
    _footballService = footballService;
    _hub = hub;
}

As we now have access to an instance of IHubContext, we can call hub methods as if we are in the hub itself.

// POST: api/Matches
[HttpPut]
[Route("api/Matches")]
public async Task UpdateMatchAsync(MatchUpdateModel model)
{
    await _footballService.UpdateMatchAsync(model);

    await _hub.Clients.All.SendAsync("RefreshMatchCentre");
}

Run the application again and it should work similarly as before and the Match Centre page should update in real-time.

Display Live Sports Updates using ASP.NET Core SignalR

Summary

SignalR is one of the coolest technology available to .NET developers. Its ability to perform tasks in real-time has made this a game-changer technology. I hope in this article, you have learned how to use SignalR and you will now use it in your projects with more confidence. If you enjoyed reading this article, please share this with your friends and colleagues.

This Post Has 13 Comments

  1. ihsan elahi

    first time in my coding career that i have tried many links of learning signalr but every time difficult for me to understand and stop to learn again .But First time your way of teaching from make me 0 to hero in a simple demo .Thanks a lot. Will never stop to learn from you. Thanks again

  2. dev

    Only one question: why are you using powerful signalr to just pass the signal to reload data and not pass the granular match update for one particular entity? Would be much neater.

    1. Waqas Anwar

      I agree with you, we can also pass one match update.
      Thanks for giving this enhancement idea to my readers.

  3. q

    where is the db script? FootballDb

    1. Waqas Anwar

      There is no script. I created a blank database in SQL Server using Management Studio.

  4. Kat Kareena

    Hi,

    Thanks for such a nice article, it’s very useful, Can you please create a demo/ article, for ex: I have a Dashboard application whenever there is a change in the few columns in different tables, I need to refresh data in DB pages.

    Thanks.

  5. Mumtaz Malik

    Its awesome, very very helpful, Sir can you write an article on signal R using Sql Dependency . Thanks a lot

  6. Ganesh

    Wow Great article please do more for us. Thanks a ton

  7. Dekel

    Hi,
    The article is great, ill try it !
    I have a beginner question.. After you joined the tables and created the “MatchViewModel”, how can you save it to sql db?
    Did you create a new table? or just used that object for client side usage? (i’m using db first in my projects).

    thanks,
    Dekel.

    1. Waqas Anwar

      I am using MatchViewModel only for creating a read only page. I am using a separate object MatchUpdateModel to update Match information in database. In a real application, there can be a back end interface of updating matches, teams, players etc. info in sports portals like these.

  8. Tahir Alvi

    Great Article,
    In one writing you cover almost all the topics related to the SignalR.

    Really appriciated.

      1. El Mehdi

        thanks a lot for this article,
        I am trying to do all this, but in insert new data my table called twice in the server

Leave a Reply