Jeff Pries

Business Intelligence, SQL Server, and other assorted IT miscellany

Custom Dashboard Development – Part 3: SignalR Design

Microsoft_.NET_Framework_v4.5_logoIn my previous posts, here and here, I discussed my objective to create a custom dashboard solution in order to meet a number of requirements as well as a method for creating that solution using a traditional Web Services approach.  In this approach, the client is wholly responsible for requesting all of its updates from the server whenever it needs them (typically on a set timer).  In this post, I’ll create the same dashboard but use a different technology, SignalR, for performing the data communications.  Much of the solution will be the same or similar to the previous solution, however the back-end is fundamentally different.

With the SignalR approach, rather than the client being responsible for requesting data updates from the server, the server maintains a connection with the client and pushes them to the client on an “as needed” basis.  This basis can be timed (such as sending an update once every 5 minutes) or it can be triggered (such as sending an update whenever data changes.)  In this example, we’ll be using the timed approach for parity with the previous solution.  As an extra benefit to the SignalR solution, when multiple clients are connected to the same dashboard, they will all display the exact same data and refresh at the exact same time (as they are not maintaining their own individual update timers.  The following diagram gives a quick illustration of the data communications between the clients, web server, and database server:

dashboard_web_model_signalr

 Laying the Foundation

The majority of this project will be the same as our previous project.  The completed Visual Studio 2013 solution along with sample database is available here.  As such, I’ll continue to be using Visual Studio 2013 as well as technologies from the previous project, such as jQuery, Json.NET, ASP.NET MVC, date.js, and jChartFX.  Additionally, we’ll be re-using the Index View almost in its entirety (we’ll be swapping out some JavaScript references) but the components will be the same.  As such, the overall look and feel of the dashboard will be identical to how it was in our previous project.  The completed dashboard will look like this:

gc_dashboard_example

 

 

Getting Started – The Front-End Client

The first difference from the previous project is that we need to add the SignalR libraries to our project.  This can be done via the NuGet package manager in Visual Studio.  By selecting the “Microsoft ASP.NET SignalR” package within NuGet, it will automatically install any other necessary dependencies for the functionality.  Note that SignalR, as it is new, is being updated fairly frequently.  As of the current writing of this article, it is at version 2.2.0.

visual_studio_signalr

Once the SignalR libraries have been added to the project, you will immediately see a Readme file which explains an additional step which needs to be taken in order to active SignalR.  This step requires you to add a new class file to the project, named Startup.cs which always contains the following:

As before, we’ll be using the same Content, images, Intellisense, and Scripts folder from the previous example.  Additionally, we’ll still be using the Dashboard.js file which was used by the previous project for data refreshes, but we’ll be rewriting it significantly for SignalR connectivity.

With all of the assets in the project, the next step is getting the front end setup is to create a Controller, which I called HomeController and then to create a View with no model or layout which I called Index (any names can be used for these, but since the default MVC route is /Home/Index I used these names for simplicity)  The Index view will be the page the web client displays which includes all of my dashboard widgets.

Within the Index, we need to do most of the client side work.  As this is a very simple project, the Index will use neither a Layout (template) or a Model (data template).  The Index file contains a link to the Dashboard.css style sheet for color information, defines all of the widget location and sizes, and links to all of the JavaScript that makes it go.  The JavaScript references are the most complex portion of the Index file and perform the following:

  • Loads jQuery, which is a core JavaScript library used for both the widgets themselves as well as the data refreshes.
  • Loads the SignalR jQuery library and SignalR hubs script, which is used for the SignalR data communications.
  • Loads date.js, which is a simple date formatting script used by the clock.
  • Loads various jChartFX libraries which draw the graphical components of the dashboard.
  • Loads the Dashboard.js script which is the primary script responsible for initiating the dashboard and SignalR.
  • Loads individual JavaScript files for each for creating and setting the style of each widget used.  This helps break the code up a little and make it more reusable.

Finally, once the document is fully loaded into the browser, it calls the “loadDashboard()” function within the Dashboard.js JavaScript file which begins the data refresh cycle.  The flow, showing the primary components — the client side Index View, the client side, Dashboard.js and SignalR scripts, and the server side SignalR functionality can be seen as follows:

 

dashboard_web_model_signalr_flow

Front-End Changes:  Dashboard.js

The core Dashboard.js file which was used in the previous project has the same ultimate goal in the SignalR project — to facilitate data updates, but it will be accomplished different.  A number of changes need to be made to the file to initiate a SignalR connection and then provide a function in the client which the server will actually call when updated data is to be received.

In the SignalR version of Dashboard.js, the “loadDashboard” still exists, but it has a reduced role.  The role of the function in this version is to start the update timer for the clock (since it is still refreshed from the client side on a timer) but no update timer for the data is set (since that is controlled on the server side.)

Instead, “init” and a “refreshChart” function are added to the document.ready section of the JavaScript file (which is called only after the document is fully loaded) and then these functions are called by the initialization of SignalR as well as by the server for periodic data refreshes.  The full code for this section is as follows:

The “$.connection.hub.start().done(init);” line initiates our SignalR connection on the server side and the server then is able to call the refresh function periodically so long as the connection is maintained.

 

Back-End (SignalR)

The back-end code for the SignalR solution, as expected, is quite different from the back-end web services code.  Instead of a single DataService.asmx file, we need two new classes.  A simple model class named DataElement.cs and the class which does most of the work, DashboardData.cs.

DataElement is nothing more than a single container class to make for a convenient way to store an element name and it’s associated data encoded in a JSON string.  This container is needed as data for all of the dashboard widgets will be stored in memory on the server in a List object.  This allows for clients which connect to the server after the refresh has occurred to receive a copy of the current data.

The DashboardData.cs file is a wholly new file which replaces DataService.asmx from the prior solution.  This class is the SignalR back-end workhorse which is responsible for maintaining the refresh timer, getting data from the database, and telling the clients to update their data.

If you flip through the DashboardData.cs file you’ll notice there is a lot going on there.  And some of it is pretty complex and foreign looking stuff.  I’ll try to hit a few of the high points here. First, variable is defined within the class for keeping track of all of the connected clients. This is necessary as the back-end will need to notify all of these clients whenever they need to update their data.

Next, a variable is defined for the storage of all dashboard widget data. These are stored within DataElements:

After establishing variables which will be used, the primary constructor fires which sets up the dashboard for the client. First, the clients list is maintained. The container containing all of the dashboard data is then cleared to make way for new data. Lastly, we loop through the dashboard elements and perform the necessary data queries to populate new DataElements with data for the widgets which are added to the tracking list. Finally, the refresh timer is started.

As the update timer fires, the UpdateChartData method is called. This performs two critical functions (with a lot of logic for ensuring they’re performed safely.) First, it calls the RefreshChartData function (via TryUpdateChartData) for each dashboard widget. This is the component which actually performs the SQL database queries. Next, it calls BroadcastDashboardData which performs the client notifications.

The BroadcastDashboardData function is actually a very simple one, but a very cool one. The function that it calls, updateDashboardData is nowhere to be found in this class. In fact, it resides within the Dashboard.js JavaScript file on the client! So the server is telling the client to run a particular function on itself. THAT is the magic of SignalR. Pretty cool stuff.

 

Conclusion

SignalR is an amazing technology with a great deal of potential.  As it is a fairly new technology, new and exciting examples are continually popping up showing its capabilities.  Once such example is this Microsoft example of how to build a chat application.  I used this example for most of my learning of how to create a SignalR application.  Do note that SignalR can change significantly between versions.  While I didn’t work directly with version 1.0 of SignalR, I did try unsuccessfully to implement some of the examples under 2.0.  Hopefully whenever 3.0 comes out, it won’t be too much different than the current version, 2.2.0.

While the rapid development of SignalR is exciting, it also means the possibilities of bugs and bumps around the way.  I first implemented a business dashboard solution with SignalR and it worked great.  But, I encountered a problem.  A problem which, ultimately, I never fully solved, but from research found that others had it as well.

I found that while SignalR worked great for a while, leaving a dashboard for a long time (such as a business day or two) would cause the server at some point to forget about the client.  And the client wouldn’t realize it.  So, since the server forgot about the client, it wouldn’t send additional updates.  And since the client didn’t know it had been forgotten, it wouldn’t know to ask.  I was able to workaround this solution by putting a hard HTML refresh meta tag in the page header.  This caused the client to fully refresh the page every few hours (I experimented with both 3 and 6 hours, 3 seemed the most reliable).  When the client performed a full refresh, it would re-establish server connectivity and prevent the server from forgetting about it.

Additionally, I’ll mention that while I created both the traditional Web Services solution and a SignalR solution for a business dashboard, I found that the Web Services dashboard was the better solution for my needs.  SignalR is great if you need to scale up to many clients or need many clients to show the same data.  I didn’t really need either of these things.  On the flip side, since Web Services data refreshes are fully handled by the client, it was a very solid solution and didn’t have the issues I experienced with SignalR not refreshing periodically.  Hopefully a future project will call for the use of SignalR as a solution (Microsoft has a great example showing its use with a live data feed showing real time Twitter data) as I look forward to working with it again.

 

Resources

 

2 Comments

  1. Jeff – Do you have an active link to download the source for this project?

  2. jpries

    May 22, 2016 at 9:52 pm

    OneDrive appears to have had an issue at some point…I’ve updated (hopefully) all of the links to new hosting. Thanks!

Leave a Reply

© 2017 Jeff Pries

Theme by Anders NorenUp ↑