If you're an avid sports fan like I am, you probably find yourself checking scores on sports websites frequently. If you're a fantasy sports fan, then you're probably glued to a scoreboard on game day. You expect to see scoring updates as soon as they occur, and you further expect your fantasy scores to update real-time. But, several sports websites auto refresh their scoreboards every 30 seconds or so. For the hardcore fanatics who want instant gratification, those 30 seconds feel like an eternity. I can't even wait those 30 seconds and often find myself hitting F5 to refresh the browser before the site auto refreshes.

Introducing real-time web functionality to a website solves the issue described above. Real-time web functionality is the ability for connected clients to receive updated information as soon as it becomes available without having to perform a new request for the update.

Sports websites are not the only place on the internet that could benefit from real-time web functionality. Real-time scenarios are all around us: chat, stock quotes, auctions, sports betting, voting/polling, gaming, etc. Continuing with the sports theme, I had the good fortune of living in the UK for 4 years where there is a sports betting parlor in almost every village. These bookkeepers also offered their services online with various ways to place bets, including in-game betting. This idea was new to me, like "next player to score", and must have been very tightly controlled as far as when the bookies would offer such a bet and when they would accept such a bet. These controls would be obvious with real-time web functionality.

Thankfully for .NET developers, the SignalR open-source library easily allows developers to introduce real-time web development to existing and new projects. SignalR is available on GitHub.

The SignalR Library

Like any server/client architecture, SignalR is comprised of a hub and connected clients. Unlike a normal request-response model, SignalR uses the following transports and continuous connection techniques depending on browser support:

  • Web Sockets - TCP connection that allows two-way traffic between client and server without new requests
  • Forever Frame - uses "chunked encoding" (not HTTP or TCP/IP) to create a continuous connection to the client so the server can send data to the client at any time
  • Server-Sent Events - HTTP connection that allows the browser to receive automatic updates from the server
  • Long Polling - HTTP connection the browser uses to request new info from the server, which holds the request open until an update is available

SignalR takes care of determining which transport to use as a connection. Additionally, SignalR uses the OWIN (Open Web Interface for .NET) interface, so developers experience a plug & play, structured library that is easy to set-up and use.

Example: Real-time Redskins Scorecard

Continuing with the sports theme, I'm a huge Redskins fan and have attended several Redskins summer camp practices where coaches call plays using players in different formations and in different situations. Wouldn't it be nice if all the coaches could score or rate players real time and then call plays using the players who have performed best during that practice? They could with .NET and SignalR. Here are the steps to create a real-time Redskins Scorecard website in .NET using SignalR.

Getting Started

Azure makes it so easy to publish a site into the Cloud, so use Visual Studio to create a new ASP.NET Application project fro the Cloud template:

Choose MVC template

I called my solution HTTR (Hail to the Redskins).

Install SignalR package via NuGet Package Manager by typing Install-Package Microsoft.AspNet.SignalR like below:

After installing SignalR, Visual Studio provides the following instruction: "To enable SignalR in your application, create a class called Startup with the following:". (Note: Startup.cs may get created for you automatically)

using Microsoft.Owin;
using Owin;
using MyWebApplication;

namespace MyWebApplication
{
 public class Startup
 {
 public void Configuration(IAppBuilder app)
 {
 app.MapSignalR();
 }
 }
} 

The Model

For this example, the list of Redskins players to rate are static values that are available to any client, but they could have just as easily been stored in a database.


using System.Collections.Generic;

namespace HTTR.Models
{
 public static class Roster
 {
 public static List Players = new List();

 public static void LoadRoster()
 {
 Players.Add(new Player { Name = "Kirk Cousins", Id = 1, Image = Links.Content.Images.KirkCousins_png, Rating = 0, Position = Position.QB });
 Players.Add(new Player { Name = "Colt McCoy", Id = 2, Image = Links.Content.Images.ColtMcCoy_png, Rating = 0, Position = Position.QB });
 Players.Add(new Player { Name = "Nate Sudfield", Id = 3, Image = Links.Content.Images.NateSudfield_png, Rating = 0, Position = Position.QB });

 Players.Add(new Player { Name = "Matt Jones", Id = 4, Image = Links.Content.Images.MattJones_png, Rating = 0, Position = Position.RB });
 Players.Add(new Player { Name = "Chris Thompson", Id = 5, Image = Links.Content.Images.ChrisThompson_png, Rating = 0, Position = Position.RB });
 Players.Add(new Player { Name = "Rob Kelley", Id = 6, Image = Links.Content.Images.FatRobKelley_png, Rating = 0, Position = Position.RB });

 Players.Add(new Player { Name = "DeSean Jackson", Id = 7, Image = Links.Content.Images.DeSeanJackson_png, Rating = 0, Position = Position.WR });
 Players.Add(new Player { Name = "Pierre Garcon", Id = 8, Image = Links.Content.Images.PierreGarcon_png, Rating = 0, Position = Position.WR });
 Players.Add(new Player { Name = "Jamison Crowder", Id = 9, Image = Links.Content.Images.JamisonCrowder_png, Rating = 0, Position = Position.WR });


 }
 }
}
namespace HTTR.Models
{
 public class Player
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Image { get; set; }
 public Position Position { get; set; }
 public int Rating { get; set; }
 }

 public enum Position
 {
 QB = 0,
 RB = 1,
 WR = 2
 }
}

The Hub

In SignalR, the Hub is the mother brain and controls all functionality on the server side. The Hub not only has access to the client making any given request (Clients.Caller) like a normal request-response environment, the Hub also has access to ALL connected clients (Clients.All).

Redskins summer camp is held in Richmond, so the RichmondHub will be the mother brain of the Scorecard application and extends the Microsoft.AspNet.SignalR.Hub class. RichmondHub contains 2 functions:

  • Rate - receives a player rating from a client, increments the rating up or down, then calls all connected clients with the new player rating
  • OnConnected - overrides the base classes method by the same name to push all current ratings to any client that has just connected
using HTTR.Models;
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;

namespace HTTR.Hubs
{
 public class RichmondHub : Hub
 {
 public void Rate(int playerId, bool thumbsUp)
 {
 Player PlayerToRate = Roster.Players.Find(p => p.Id == playerId);
 PlayerToRate.Rating += thumbsUp ? 1 : -1;

 Clients.All.updateRating(playerId, PlayerToRate.Rating);
 }

 public override Task OnConnected()
 {
 // Send current rating to newly joined client 
 Clients.Caller.hailToTheRedskins(Roster.Players);
 return base.OnConnected();
 }
 }
}

The Client View

The view simply loops through the Players in the model (by position) and displays a thumbs up & thumbs down button for rating. The most important thing to note here is the order of the scripts at the bottom of the view.


@model List
@using HTTR.Models

@{ 
 ViewBag.Title = "Redskins Scorecard 2016";
}

Redskins Scorecard

@foreach (var PositionPlayers in Roster.Players.OrderBy(x=>x.Position).GroupBy(x => x.Position).ToList()) {

@PositionPlayers.Key.ToString()

@foreach(Player Player in PositionPlayers) {

@Player.Name

Rating: @Player.Rating
}
} @section scripts { }

The Client Script

SignalR allows the Javascript client to reference the persisted connection to the hub using $.connection. It knows about any Hubs available as well as allowing the ability for the client to handle hub events. Additionally, client specific methods can be defined in the client that can then be referenced in the hub.

In Scorecard example, a reference is made to the RichmondHub using the $.connection reserved reference provided by SignalR. Methods are defined against it's .client property to updateRating and to initialize all ratings when first connected via hailToTheRedskins. See the Hub code above to see how the Hub references the .Caller or .All to invoke these methods. The client can also handle events, like once the connection has started.


$(function () {

 var richmondHub = $.connection.richmondHub;

 $.connection.hub.start().done(function () {
 $(".thumbsUp").click(function () {
 richmondHub.server.rate(this.id, true);
 });
 $(".thumbsDown").click(function () {
 richmondHub.server.rate(this.id, false);
 });
 });

 richmondHub.client.updateRating = function (id, rating) {
 $("span[id='" + id + "']")[0].textContent = rating;
 }

 richmondHub.client.hailToTheRedskins = function (players) {
 for (i = 0; i <= players.length - 1; i++) {
 $("span[id='" + players[i].Id + "']")[0].textContent = players[i].Rating;
 }
 }

});

Demo

After publishing the HTTR solution to an Azure website, this demonstration shows the SignalR site accessed from 3 different clients: Chrome (desktop), Firefox (desktop) & Chrome (Android phone). As I update the rating on one client, you can see the other clients update without being refreshed. All three client are used to post updates and all 3 reflect all updates.

View a short demo of the Redskins Scorecard site using SignalR on Vimeo here.

Additional Resources