Want to try a multi-user demo?

Make sure to open the page in multiple browsers, click on a book and hit Save. You will see the change simultaneous in each browser.

Books

Add a new book

Using SignalR for real time data updates

Traditionally HTML based CRUD would generate all HTML on the server based on the user selecting something and then using an HTML form and an HTTP POST to send the changes back to the server. The newer generation of applications might just send the HTML skeleton to the client and some client side JavaScript would run, load the data and display it to the user. Updates would be done in a similar fashion with some JavaScript. To be fair the JavaScript would be using jQuery in most cases and would be quite trivial to write against some REST service.

However both these approaches have one fundamental problem. When a user requests some data he has a local copy of that in the browser. If another user now updates that same entity on the server the first user still has the old stale data in front of him. And that will remain so until reloads the data. Of course we can add some code to actively check for changes to the data but usually there will be no change so that results in a lot of useless network traffic.

Replacing a REST service with SignalR

Suppose the client doesn’t load the data using a REST service but uses SignalR instead. Doing request/response asynchronous style communication with SignalR is easy, a little different from using jQuery but no big difference. In the following code the clients starts the communications hub and passed the loadBooks callback to load the books as soon as the communications are initialized.

$(function () {
    var hub = $.connection.booksHub;
    $.connection.hub.start(loadBooks);
    $("#load").click(loadBooks);
 
    function loadBooks() {
        hub.getBooks().then(function (books) {
            var ul = $("#books");
            ul.empty();
            $.each(books, function () {
                var newLi = $("<li>").appendTo(ul);
                renderBook(newLi, this);
            });
        });
    };
 
    function renderBook(li, book) {
        $(li).text(book.Title + " by " + book.Author)
            .attr("data-id", book.Id)
            .data("title", book.Title)
            .data("author", book.Author);
    }
}

 

The C# code on the server is no big deal either and hardly any more complex then the previous chat application.

public class BooksHub : Hub
{
    private IBooksRepository _repo = new BooksRepository();
 
    public IEnumerable<Book> GetBooks()
    {
        var books = _repo.GetBooks();
        return books;
    }
}

 

Push notifications after updates

So far we have not achieved much, the user will still be looking at stale data when the data on the server is updated by another user. But SignalR uses persistent connections and can easily push changes to each connected client when that happens. All we need to do is add a bit of client side code to send updates and handle the change notifications.

 

$("#updateBook").submit(function (e) {
    e.preventDefault();
    var form = $(this);
 
    var book = {
        id: $("#id", form).val(),
        title: $("#title", form).val(),
        author: $("#author", form).val()
    };
    hub.updateBook(book);
});
 
hub.bookUpdated = function (book) {
    var form = $("#updateBook");
    form.slideUp();
    var li = $("li[data-id=" + book.Id + "]");
    if (li.length === 0) {
        var li = $("<li>").appendTo("#books");
    }
    renderBook(li.get(0), book);
    li.animate({ "background-color": "Yellow" }, 500, function () {
        li.css("background-color", "")
    });
}

 

The first method sends changes to the server, this is pretty basic clients side JavaScript code. The second bookUpdated function is called whenever the server broadcasts a change to all clients. This function first tries to locate the updated book and to update it on screen. If the book isn’t found it adds it to the bottom of the list.

 

Supporting this on the server using a SignalR hub is easy. Below is the complete C# source code for the hub that servers up data, accepts updates and inserts and broadcasts those updates and inserts to all connected clients.

public class BooksHub : Hub
{
    private IBooksRepository _repo = new BooksRepository();
 
    public IEnumerable<Book> GetBooks()
    {
        var books = _repo.GetBooks();
        return books;
    }
 
    public void updateBook(Book book)
    {
        var newBook = _repo.UpdateBook(book);
        this.Clients.bookUpdated(newBook);
    }
 
    public void addBook(Book book)
    {
        var newBook = _repo.AddBook(book);
        this.Clients.bookUpdated(newBook);
    }
}

 

With this addition each user is automatically notified of any change anyone makes and will see the data in the browser update dynamically. For some extra effects I have added a small jQuery animation to the record being updated on the client.