Live Update

Live Update

Live Update is a mode that provides synchronous data update in real time. When someone of users makes a change, it becomes visible to all others immediately.

Live Update with SignalR

Server-side

  • Firstly, we install the SignalR library. The easiest way to install the library is to run the following command in the NuGet manager console:

    PM> Install-Package Microsoft.AspNet.SignalR
    
  • Then, we create a hub (SignalR will handle all the connection stuff on both client and server sides, ensuring that the connection stays open and active):

    using Microsoft.AspNet.SignalR.Hubs;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.Hubs;
    namespace Scheduler.SignalR.Sample
    {
        [HubName("schedulerHub")]
        public class SchedulerHub : Hub
        {
            public void Send(string update)
            {
                this.Clients.All.addMessage(update);
            }
        }
    }

    By setting this.Clients.All.addMessage(update), we are telling all the clients to call the addMessage() function when they make a change in the scheduler. addMessage() is called from the server via our persistent connection.

  • We add new startup class: right-click the project, then click Add → New Item → OWIN Startup Class. If your version of the Visual Studio does not have a template for OWIN Startup Class, simply create an empty class named Startup with following code:

    using Microsoft.Owin;
    using Owin;
    [assembly: OwinStartup(typeof(Scheduler.SignalR.Sample.Startup))]
    namespace Scheduler.SignalR.Sample
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                app.MapSignalR();
            }
        }
    }

Client-side

  • We add references to jQuery and jQuery.signalR:

    <script src="Scripts/jquery-3.1.0.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-2.2.1.min.js"" type="text/javascript"></script>
  • We add the reference to dynamically generated script that will contain the client hub:

    <script src="/signalr/hubs" type="text/javascript"></script>
    
  • Finally, we initialize the hub connection and attach it to the scheduler's dataprocessor:

    var hub = $.connection.schedulerHub;
     
    scheduler.dataProcessor.attachEvent("onLocalUpdate", function (data) {
         hub.server.send(JSON.stringify(data));
    });
    hub.client.addMessage = function (message) {
         scheduler.dataProcessor.applyChanges(JSON.parse(message));
    };
     
    $.connection.hub.start();

    When some change occurs in the scheduler, the onLocalUpdate event occurs and the dataprocessor passes action data to the hub.server.send method. The client hub receives this data and call the addMessage function which parses the message data, apply changes to the scheduler and share information with the other clients.

<!DOCTYPE html>
<html>
<head>
    <title>Scheduler SignalR</title>
    <script src="@Url.Content("~/Scripts/jquery-3.1.0.js")" ></script>
    <script src="@Url.Content("~/Scripts/jquery.signalR-2.2.1.min.js")" ></script>
    <script src="@Url.Content("~/signalr/hubs")" ></script>
 
</head>
<body>
@Html.Raw(Model.Render())
<script>
    (function () {
        var hub = $.connection.schedulerHub;
 
        scheduler.dataProcessor.attachEvent("onLocalUpdate", function (data) {
            hub.server.send(JSON.stringify(data));
        });
        hub.client.addMessage = function (message) {
            scheduler.dataProcessor.applyChanges(JSON.parse(message));
        };
 
        $.connection.hub.start();
    })()
</script>
</body>
</html>

Common realization

Scheduler configuration

To work in the Live Update mode, scheduler can be initialized and configured in a standard way (see details in the related article Simple ASP.NET application with Scheduler).
The only addition required is enabling the LiveUpdates extension:

using System;
using System.Web;
using System.Web.Mvc;
 
using DHTMLX.Scheduler;
using DHTMLX.Common;
using DHTMLX.Scheduler.Data;
namespace LiveUpdateScheduler.Controllers
{
    public class Home: Controller
    {
        public ActionResult Index()
        {
            var scheduler = new DHXScheduler(this);
            scheduler.Extensions.Add(SchedulerExtensions.Extension.LiveUpdates); //this line enables the LiveUpdates extension
            scheduler.LoadData = true;
            scheduler.EnableDataprocessor = true;
 
            return View(scheduler);
        }
 
        public ContentResult Data()
        {
           ...
        }
 
        public ContentResult Save(int? id, FormCollection actionValues)
        {
            ...
        }     
    }
}

Server-side

On the server-side we should provide a persistent connection. There are several variants here and we consider 2 of them:

  • WebSockets (not require any third-party libraries but natively supported only in IIS 8+, Windows 8 or Windows Server 2012. Support for browsers: Firefox 15+, Chrome 22+, Safari 6+, Opera 21+, IE 10+)
  • SignalR async library (universal way and supported by all browsers)

Client-side

On the client-side we initialize web socket connection and process incoming messages with the help of the scheduler.dataProcessor class API

Live Update with WebSockets

Client-side

  • We initialize web socket connection and attach it to the scheduler's dataprocessor (we use the 'onLocalUpdate' event and the 'applyChanges' method of DHXScheduler.dataProcessor that are defined in the LiveUpdates extension):

    var socket = new WebSocket('ws://@Request.Url.Authority/api/WebSocket');
    scheduler.dataProcessor.attachEvent("onLocalUpdate", function (data) {
         socket.send(JSON.stringify(data));
    });
    socket.onmessage = function (ev) {
        scheduler.dataProcessor.applyChanges(JSON.parse(ev.data));
    };

    The onLocalUpdate event occurs each time a user makes a change.
    So, when some change occurs in the scheduler, the dataprocessor passes action data to the WebSocket.send method. The socket receives this data and call the onmessage event handler which parses the message data, apply changes to the scheduler and share information with the other clients.

<!DOCTYPE html>
<html>
<head>
    <title>Scheduler & WebSockets</title>
</head>
<body>
    @Html.Raw(Model.Render())
    <script>
        (function(){
            var socket = new WebSocket('ws://@Request.Url.Authority/api/WebSocket');
            scheduler.dataProcessor.attachEvent("onLocalUpdate", function (data) {
                socket.send(JSON.stringify(data));
            });
            socket.onmessage = function (ev) {
                scheduler.dataProcessor.applyChanges(JSON.parse(ev.data));
            };            
        })()
    </script>
</body>
</html>

Server-side code

  • Firstly, we create a collection to store all connected clients. We do it right in the Global.asax.cs:

    using System.Linq;
    using System.Web;
    using System.Web.Http;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Microsoft.Web.WebSockets;
    namespace Sockets
    {
        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
     
                Application["connections"] = new WebSocketCollection();
     
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
            }
        }
    }
  • Secondly, we create a class to handle web sockets connections:

    using System;
    using Microsoft.Web.WebSockets;
    public class DHXWebSocketListener : WebSocketHandler
    {
        public DHXWebSocketListener(WebSocketCollection clients)
        {
            connections = clients;
        }
        private readonly WebSocketCollection connections;
        public override void OnOpen()
        {
            connections.Add(this);
        }
     
        public override void OnClose()
        {
            connections.Remove(this);
        }
     
        public override void OnMessage(string message)
        {
            connections.Broadcast(message);
        }
    }

    The code does the basic routine - adds/removes a socket to/from application's socket connection collection. When a socket receives a new message - sends it to the other clients.

  • And finally we need a controller to handle new clients connections:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using Microsoft.Web.WebSockets;
     
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
     
    namespace Sockets.Controllers
    {
        public class WebSocketController : ApiController
        {
            public HttpResponseMessage Get()
            {
                HttpContext.Current.AcceptWebSocketRequest(
                    new DHXWebSocketListener(
                        (WebSocketCollection)HttpContext.Current.Application["connections"])//global list of connections
                );
                return new HttpResponseMessage(
                    HttpStatusCode.SwitchingProtocols
                );
            }
        } 
    }

comments powered by Disqus