Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint

Using SignalR

I think we all will agree that Web Sockets is pretty cool. The type of bidirectional communication it affords opens up a world of opportunity. Unfortunately, support for Web Sockets is still spotty. So, while Web Sockets is certainly a glimpse of the future, it may not seem like a viable option for the systems you are building today. If only we had some way to take advantage of Web Sockets when it is available, but fall back to other options when it isn’t.

As I’m sure you have guessed, this is certainly achievable, and one option for doing so is built into a set of libraries named SignalR. SignalR is something that two Microsoft employees started as an open source project that was later adopted by Microsoft as an officially sponsored project. SignalR makes it easy to build real-time solutions using asynchronous signaling approaches. SignalR makes it possible for us to build modern solutions today that will automatically take advantage of Web Sockets when available.

So what does SignalR do when Web Sockets support isn’t available? It checks for a few different options, and uses the one that is best suited to the task at hand. Web Sockets is checked first. If that isn’t supported then Server-Sent Events, Forever Frame, and Long Polling are checked.

Note

SignalR isn’t the only game in town when it comes to falling back to other techniques such as Forever Frame and Long Polling. Socket.IO and NowJS are a few options that do this as well. However, SignalR goes about it in an easier way and is specifically designed to play well with ASP.NET.

Building a Persistent Connection Example

Two primary options are available for building the SignalR server-side aspects. The first is a persistent connection and the second is a hub. The persistent connection option is pretty similar to what we’ve seen in the Fleck and IIS 8 web socket server implementations provided in this chapter.

As you’ll see, it doesn’t take much code to get this set up. You start by installing the SignalR NuGet package. After that, it’s a simple matter of adding a custom route map and implementing a class that derives from PersistentConnection. A snippet from a Global.fs file with the new code emphasized is shown in the following example:

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterRoutes(routes:RouteCollection) =
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )

    member this.Start() =
        RouteTable.Routes
            .MapConnection<ChartServer>("chartserver", 
                                        "chartserver/{*operation}") |> ignore

        AreaRegistration.RegisterAllAreas()
        Global.RegisterRoutes(RouteTable.Routes)

The new class that inherits PersistentConnection is shown in the next example. As I mentioned, it’s not that much different from the previously shown IIS 8 web socket server examples. Here we’re simply overriding the OnReceivedAsync method rather than the OnMessage method:

module SignalRExample

open System
open SignalR
open Newtonsoft.Json

// Code from previous examples removed for brevity

type ChartServer() =
    inherit PersistentConnection()

    override x.OnReceivedAsync(request, connectionId, data) = 
        votesAgent.PostAndReply(fun reply -> Message.Vote(data, reply))   
        |> Seq.map(fun v -> { language = fst v; count = snd v } )
        |> JsonConvert.SerializeObject 
        |> base.Connection.Broadcast

A JavaScript Client

The client-side aspects of SignalR change slightly depending on whether the client is using JavaScript or F# as well as whether the server is using PersistentConnection or Hub. Additionally, the JavaScript option is a little bit different from the previously seen web socket client-related JavaScript. This difference is partially due to the jQuery plug-in that helps SignalR do its magic. Here is a JavaScript example for the PersistentConnection that we built in the earlier example:

/* Some code removed for brevity */
    
var connection = $.connection("/chartserver");

connection.received(function (data) {
    updateChart($.parseJSON(data));
});

$('#vote').click(function (event) {
    var vote = $('input:radio[name=langOption]:checked');

    if (vote.length) {
        connection.send(vote.val());
    };

    $pages.hide();
    $("#results").toggle();

    event.preventDefault();
});

connection.start();

Using the client and server together results in the same effect we achieved in the previous web socket examples. The output looks just like Figure 4-2.

An F# Client

What if you want to connect to a SignalR server from F# instead of JavaScript? That’s no problem at all. The next example shows an F# console application that can cast a vote for a favorite programming language.

To build this, create a new F# console application, install the SignalR.Client NuGet package, and add code such as the following:

module SignalRExample

open System
open SignalR.Client

let connection = 
    Connection "http://localhost:2920/chartserver"

connection.Start().Wait()

connection.Send "F#" 
|> Async.AwaitIAsyncResult 
|> Async.Ignore 
|> ignore

connection.Stop() |> ignore

printfn "Vote cast for F#"
Console.ReadLine() |> ignore

Constructing a Hub Example

As I mentioned previously, SignalR provides another option, which is known as a Hub. This option provides a higher-level abstraction over PersistentConnection. This abstraction makes it possible for the server to call named JavaScript functions. Let’s look at an example.

The server side

The next example provides a self-hosted SignalR Hub. It’s an F# console application that has had the SignalR.Hosting.Self package installed. To set up the hub, you create a class that inherits from Hub. You then add the methods you wish to be available for calls from clients. To call functions declared in JavaScript, you can use the Clients dynamic object followed by the name of the desired function. This code is emphasized in the following example:

// Code from previous examples removed for brevity

type ChartHub() =
    inherit Hub()
    member x.Send (data:string) = 
       let result = 
           votesAgent.PostAndReply(fun reply -> Message.Vote(data, reply))   
           |> Seq.map(fun v -> { language = fst v; count = snd v } )
       try
           base.Clients?updateChart(result)
       with
       | ex -> 
           printfn "%s" ex.Message

let server = Server "http://*:8181/"
server.MapHubs() |> ignore

server.Start()

printfn "Now listening on port 8181"
Console.ReadLine() |> ignore

One thing you may have noticed in the preceding example is the use of the ? operator when working with the dynamic Clients object. F# doesn’t provide the exact same dynamic object option that is provided in C#. Instead, it provides a more powerful option that allows you to implement whatever dynamic functionality you require. This option is provided in the form of dynamic lookup operators.

Dynamic lookup operators can provide the same functionality as what is offered in C#, though they’re not restricted to that functionality only. You can customize the implementation of dynamic lookup operators in whatever way you see fit, allowing power beyond that of its cousin language. One implementation that has gained popularity is available via the ImpromptuInterface.FSharp library, which can be installed with a NuGet package ID of the same name. The Hub example uses this library.

The client side

The JavaScript code changes slightly when using a Hub as well. Here’s the example we have been reviewing, with the changes that are required to interact with a Hub emphasized:

$.connection.hub.url = 'http://localhost:8181/signalr'

var chartHub = $.connection.chartHub;

chartHub.updateChart = function (data) {
    updateChart(data);
};

$('#vote').click(function (event) {
    var vote = $('input:radio[name=langOption]:checked');

    if (vote.length) {
        chartHub.send(vote.val())
    }

    $pages.hide();
    $("#results").toggle();

    event.preventDefault();
});

$.connection.hub.start();

One other key thing we have to do to make this work is to add a few script references such as the following:

<script src="@Url.Content("~/Scripts/jquery.signalR-0.5.2.min.js")"></script>
<script src="http://localhost:8181/signalr/hubs" type="text/javascript"></script>
  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint