Display Live Sports Updates using ASP.NET Core SignalR
Tags: signalr, webdev, aspnetcore, realtime
Published: 2021-10-21 16:54:47

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. 

[Download Source Code](https://github.com/ezzylearning/SignalRLiveSportsDashboardDemo)

![Display Live Sports Updates using ASP.NET Core Signal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6ancthehhglhu33wb8y.png)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxmddvexeewhiqbribo8.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fspobhavahxewe9fdx18.png)

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

| **Browser** | **Version** |
| --- | --- |
| Apple Safari, including iOS | Latest Version |
| Google Chrome, including Android | Latest Version |
| Microsoft Edge | Latest Version |
| Mozilla Firefox | Latest 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.

| Method | Description |
| --- | --- |
| All | Calls a method on all connected clients |
| Caller | Calls a method on the client that invoked the hub method |
| Others | Calls a method on all connected clients except the client that invoked the method |
| AllExcept | Calls a method on all connected clients except for the specified connections |
| Client | Calls a method on a specific connected client |
| Clients | Calls a method on specific connected clients |
| Group | Calls a method on all connections in the specified group |
| GroupExcept | Calls a method on all connections in the specified group, except the specified connections |
| Groups | Calls a method on multiple groups of connections |
| OthersInGroup | Calls a method on a group of connections, excluding the client that invoked the hub method |
| User | Calls a method on all connections associated with a specific user |
| Users | Calls 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.

![SignalR - Admin Page with Updated Goals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3edkr8ytl2d8rih7j2rm.png)

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0pfpa1nezw2wjy9zjfj.png)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9w73mrnes0p2znagaej.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcfyi7esi6ascgjd4b4o.png)

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)](https://www.ezzylearning.net/tutorial/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.

- [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer/) 
- [Microsoft.EntityFrameworkCore.Design](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design) 
- [Microsoft.EntityFramewo](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/)[r](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/)[kCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/)

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](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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6zyu7cvdx344itoasf32.png)

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](https://www.ezzylearning.net/tutorial/a-developers-guide-for-creating-web-apis-with-asp-net-core-5)

## 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 **&lt;tbody&gt;** element. The important thing to note is that the **&lt;tbody&gt;** 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3edkr8ytl2d8rih7j2rm.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53cu3ijj0kt7pajzq35x.png)

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.

![Display Live Sports Updates using ASP.NET Core Signal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6ancthehhglhu33wb8y.png)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/id0jukx1x0ouqzy0g2qu.png)

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](https://www.ezzylearning.net/tutorial/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48l4uyl6c0zhgnmv98h6.png)

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 Signal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6ancthehhglhu33wb8y.png)

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

![Live Sports Updates using ASP.NET Core SignalR](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8jeeyokhvy6marbvp68.png)

## 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 Signal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6ancthehhglhu33wb8y.png)

## 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.