SignalR is actually a library that simplifies the creation and management of persistent connections among internet servers and clientele. This facilitates the development of programs that may screen updates to info held around the server in real-time. Chat apps will be the most evident beneficiaries of this technology, but line-of-business apps that need to report availability to customers can reward also. Listed here, I take a look at extending the canonical SignalR chat example to incorporate a "who's typing" feature, and that i also prolong my prior Knockout instance to make use of SignalR.


Very first, what is the trouble that SignalR is made to remedy? The net functions on the Request-Response design. Browsers along with other person brokers make requests and net server offer a reaction to that request.The reaction is distributed for the delivery tackle supplied within the ask for by the person agent. And that is the natural purchase of issues on the web - servers can not make responses without a request. For your most element, this is not a concern, but if you need to display real-time updates with your net webpage, you have required to vacation resort to methods like frequently polling the server making use of AJAX to determine if any modifications experienced been produced to info. Alternatively, you can use Comet engineering, which retains a persistent connection open between the server and the customer. HTML5 introduced two new techniques - Server Despatched Occasions and WebSockets. SignalR is really a user-friendly wrapper about every one of these technologies which makes it a lot much easier to develop apps that need the real-time show of knowledge. SignalR utilises HTML5 Web Sockets API where it is obtainable, and falls again onto other technologies where they may be not - Server Sent Events, Eternally Frames or Long Polling, the final two of which are Comet strategies.

SignalR is currently in pre-release - Release Prospect one to be precise. It is obtainable by way of Nuget or Github. Nevertheless, as it is pre-release, it is not obtainable by way of the WebMatrix Nuget client. But you can get it using Visible Studio (and Express For Net edition, which is free) if you pick "Include Prerelease" as an alternative of the default "Stable Only" choice. Or you can use the Package Manager Console and simply sort:

Install-Package Microsoft.AspNet.SignalR -Pre


It is suggested to set up the package by doing this (via Nuget) as you'll find quite a few bits and pieces to be set up:

The centre bit of a SignalR application is actually a hub, that is similar to a Controller in ASP.Net MVC. A hub is accountable for getting enter and creating output. Hubs are created as server-side classes that inherit from Microsoft.AspNet.SignalR.Hubs.Hub. Public methods created within a hub course are meant to be known as from consumer code. They normally outcome inside a reaction becoming despatched to the consumer. Here's the hub and technique that the Chat example in the ASP.Net internet web site features:

using Microsoft.AspNet.SignalR.Hubs;

public class ChatHub : Hub
{
    public void Send(string name, string message)
    {
        Clients.All.broadcastMessage(name, message);
    }
}


This should be saved as ChatHub.cs (a C# class file) in App_Code. A making use of directive is extra to the top in the file that makes Microsoft.AspNet.SignalR.Hubs available to the code. At runtime, a JavaScript file is produced dynamically which consists of a client-side implementation from the public Deliver technique in order that it may act as a proxy towards the server-side method. You write an implementation from the broadcastMessage approach in client-code in order that the Hub method can call it and therefore supply a reaction. You'll be able to also decide who ought to get the response through the choices obtainable:


Here's the full client-side code starting with the Layout page:

@{
    
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@Page.Title</title>
        <link href="~/Styles/site.css" rel="stylesheet" type="text/css" />
        <script src="~/Scripts/jquery-1.6.4.min.js" ></script>
        <script src="~/Scripts/jquery.signalR-1.0.0-rc1.min.js"></script>
        <script src="~/signalr/hubs"></script>
        @RenderSection("script", required: false)
    </head>
    <body>
        @RenderBody()
    </body>
</html>


Notice that there is a reference to a script called hubs in a signalr folder, neither of which exist. This is a placeholder for the dynamically generated script that translates the Hub methods to client code.

 

@{

}

    <div class="container">
        <input type="text" id="message" />
        <input type="button" id="sendmessage" value="Send" />
        <input type="hidden" id="displayname" />
        <ul id="discussion"></ul>
    </div>

@section script{
    <script type="text/javascript">
        $(function () {
 
            var chat = $.connection.chatHub;

            chat.client.broadcastMessage = function (name, message) {
                $('#discussion').append('<li><strong>' + name
                    + '</strong>:&nbsp;&nbsp;' + message + '</li>');
            };

            
            $('#displayname').val(prompt('Enter your name:', '')); 
            $('#message').focus();
            $.connection.hub.start().done(function () {
                $('#sendmessage').click(function () {
                    var encodedName = $('<div />').text($('#displayname').val()).html();
                    var encodedMsg = $('<div />').text($('#message').val()).html();
                    chat.server.send(encodedName, encodedMsg);
                    $('#message').val('').focus();
                });
            });
        });
    </script>
}

Some HTML factors are produced at the top of the file - a textual content box, button, a hidden area and an unordered checklist. Inside the script block, a client-side proxy is developed for your server-side ChatHub class:

var chat = $.connection.chatHub;

The client-side model in the hub is generated utilizing camel-case (first word lower scenario). Remember that you have to write down your personal client-side implementation of the dynamic broadcastMessage approach? The following section displays that. It requires the two strings offered from the Hub approach, and uses them to generate a list item which is additional for the unordered list. The subsequent few lines create the user by prompting to get a title which can be stored in the concealed discipline, and placing the main target within the textbox. Then the link to the hub is opened, along with a simply click function handler is additional to the button which leads to the Deliver approach inside the server edition of the hub becoming invoked:

chat.server.send(encodedName, encodedMsg);

So just to recap, the button click invokes the client-side chat.server.send method, which calls the public ChatHub.Send method, which responds with Clients.All.broadcastMessage which invokes the client-side chat.client.broadcastMessage function.

There is one final thing needed to get this working, asn that is to register the default route for hubs, which is best done in an _AppStart.cshtml file:

@using System.Web.Routing
@using Microsoft.AspNet.SignalR

@{
    RouteTable.Routes.MapHubs();
}

The chat example is easy to understand and illustrates the basic workflow nicely. But it is not a complete application by any stretch of the imagination. It needs more, and a common feature of chat applications is an indicator of who is currently typing. We can use the workflow logic above to plan an implementation.

Something on the client needs to invoke a server-side method. Keystrokes are as good an indication of typing as any, so that will do:

$('#message').keypress(function () {
    var encodedName = $('<div />').text($('#displayname').val()).html();
    chat.server.isTyping(encodedName);
});

From the definition of the client method, it should be easy to discern the name and signature of the additional method required in the ChatHub class:

public void IsTyping(string name){
    Clients.All.sayWhoIsTyping(name);
}

With each keypress, the name of the current user is passed to the server-side IsTyping method, which responds by calling a client-side function called sayWhoIsTyping:

chat.client.sayWhoIsTyping = function (name) {
    $('#isTyping').html('<em>' + name + ' is typing</em>');
    setTimeout(function () { 
        $('#isTyping').html('&nbsp;');
    }, 3000);
};

It's a rough and ready function, which takes the name passed from the server-side method, and displays it as part of a string in a div with an ID of isTyping. The text is cleared after 3 seconds.

SignalR with Knockout

I've created about Knockout prior to. In case you are undecided what Knockout is all about, you should go through the previous article first. In that prior write-up, the instance exhibits a straightforward software in which the user can choose a goods, and in the ensuing listing, choose 1 item to check out the small print. There is certainly an easy button that results within the ViewModel's unitsInStock worth reducing by one as if a obtain were created, as well as the UI being up-to-date because of this. And that is among the advantages of Knockout - adjustments to observable values inside the ViewModel result in that modify being mirrored wherever it's certain within the UI. Nevertheless the alter is localised to the personal user's browser. If a product unit is actually obtained, or indeed if more are made accessible, it might be considered a really good concept if ALL existing users are notified in the change in unitsInStock in real-time, right? Seems like a activity for SignalR.

The quantity of adjustments required for the existing Knockout sample code are actually quite tiny. I amended the existing ViewModel purchase perform and added an additional operate to manage reordering products. I also additional two computed observables (they used to be known as dependent observables); one which functions out if the consumer can purchase an item based on availability, and the other that works out if the reorder degree continues to be achieved:

viewModel.buy = function() {
    var id = this.productId();
    productsHub.server.buyProduct(id);
};

viewModel.reorder = function() {
    var quantity = this.reorderQuantity();
    var id = this.productId();
    productsHub.server.reorderProduct(quantity, id);
};

viewModel.canBuy = ko.computed(function() {
    return this.unitsInStock() > 0;
}, viewModel);

viewModel.canReorder = ko.computed(function() {
    return this.unitsInStock() <= this.reorderLevel();
}, viewModel);

I also added a reorderLevel home towards the ViewModel, and after that extra a textbox and button for reordering, and wired up its visibility for the canReorder computed observable. The reordering button and textbox will only be seen in the event the units in inventory reaches the reorder level price. The Acquire A single button is currently only enabled if you will find units in inventory. Earlier, when there were no models in stock, the end result of clicking the Buy A single was an inform indicating that no inventory existed:

<div class="row" data-bind="visible: canReorder">
    <span class="label"><input data-bind="value: reorderQuantity" class="reorder" /></span>
    <span><button data-bind="click: reorder">Reorder</button></span>
</div>
<div class="row">
    <span class="label">&nbsp;</span>
    <span><button data-bind="click: buy, enable: canBuy">Buy One</button></span>
</div>

When you have been following alongside to date, you'll be able to see that the two ViewModel features: purchase and reorder stage to server-side Hub methods that don't at present exist: BuyProduct (indicated by productsHub.server.buyProduct) and ReorderProduct (indicated by productsHub.server.reorderProduct). So listed here is the code for your ProductHub with its methods:

using Microsoft.AspNet.SignalR.Hubs;
using WebMatrix.Data;

public class ProductHub : Hub
{
    public void BuyProduct(int productId) {
        using (var db = Database.Open("Northwind")) {
            var unitsInStock = db.QueryValue("SELECT UnitsInStock FROM Products WHERE ProductId = @0",
 productId);
if (unitsInStock > 0) { db.Execute("UPDATE Products Set UnitsInStock = UnitsInStock - 1 WHERE ProductId = @0",
productId); unitsInStock -= 1; } Clients.All.updateAvailableStock(unitsInStock, productId); } }
public void ReorderProduct(int quantity, int productId){ using (var db = Database.Open("Northwind")) { db.Execute("UPDATE Products Set UnitsInStock = UnitsInStock + @0 WHERE ProductId = @1",
quantity, productId);
var unitsInStock = db.QueryValue("SELECT UnitsInStock FROM Products WHERE ProductId = @0",
productId); Clients.All.updateAvailableStock(unitsInStock, productId); } } }

This time, the adjustments brought on by user motion will likely be saved within the database, which did not occur in the prior Chat example. The BuyProduct approach retrieves the existing quantity of units in stock for the chosen product, and permits a purchase if you'll find are any. The revised quantity of units in stock, combined with the product ID are sent towards the all currently linked clientele by way of an updateAvailableStock technique. The ReorderProduct method simply raises the UnitsInStock value by whatever quantity was handed in towards the method from the ViewModel function. Once more, the revised variety of units in stock is broadcast to all related customers.

So how can the customer deal with this broadcast? It requirements an updateAvailableStock perform in order that the hub methods can get in touch with it:

productHub.client.updateAvailableStock = function(unitsInStock, productId) {
    if (viewModel.productId() == productId) {
        viewModel.unitsInStock(unitsInStock);
        $('span[data-bind="text: unitsInStock"]').effect('highlight', { color: '#9ec6e2' }, 3500);
    }
};

First is checks to see when the ID in the solution being up to date may be the 1 presently sure to the ViewModel, and when it is, the worth is updated as well as a emphasize result is used towards the span that shows the models in inventory:


ll that remains to do is to create a client-side proxy for the ProductHub and establish a connection:

productsHub = $.connection.productHub;

$.connection.hub.start();


Both of these examples are available as part of a sample site available at GitHub.

SignalR is a lot more powerful than these introductory demonstrations illustrate. You can write your own PipelineModule class and inject your own processing code into the hub pipeline. You can add authorization really easily to a hub method or a complete hub using data annotiation style attributes.