Using SignalR for real time data updates

In a previous post I showed how easy it is to create a simple chat application using SignalR. And chatting on the internet might be popular but as it turns out there are rather a lot of applications that need to do more than just chat. As it is there are a lot more CRUD style applications, where users edit data usually stored in a database for some purpose, than chat applications. So in this blog post I am using a simple CRUD, without the delete part, application to show the power of SignalR.

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.



 



Want to try?



If you want to try this I have a live demo on my site where you can test this SignalR style page. 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.



 



Conclusion



I think doing push updates like this will become more and more popular over the next year. just imagine the power of updating websites real time when people are browsing, not when they refresh but as they read. Maybe Amazon would do something like detect you are staying on the same book page and decided to drop the page and animate the effect while you are reading. Sounds like a nice way to get a few percent extra sales right?



 



Enjoy!

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>