Introduction
Sometimes, server side operations take time. It's not really user friendly to have users pressing a button and waiting 30 seconds for some operation to be executed, with the only progress information being a spinning wheel.
If you have server side operations that are time consuming and you would like your users to get insight into what's going on in the server in real-time, this tip is for you.
I will show how you can use SignalR 2.0 to send messages from the server to the client while a process is running in the server. The result is a notification bar that is displayed to the user showing the progress happening in the server.
Disclaimer
The fact that you can use such technique doesn't mean you should start using it everywhere. You should always strive to optimize performance. As an example, if you have a database query that is slow, you should try to optimize it first and, if it's really an expensive operation that you cannot improve, you can use such a technique to make your users more informed about the operation in progress.
Using the Code
For creating the sample application I describe below I used Visual Studio 2013, jQuery 1.10, SignalR 2.0 and noty 2.1.
First of all, we need to add SignalR to the web application. There are several ways to do this, the simplest being right-clicking in your project, Manage NuGet packages, search Online for signalr and selecting to install Microsoft ASP.NET SignalR.
We are going to use a notification jQuery plugin that provides nice out-of-the-box notifications. Still in NuGet's package management window, search for "noty". Select and install the package named jQuery Notification Plugin.
Next, we add to the project a OWIN Startup class.

Add the following line to the Configuration
method of the new Startup
class:
app.MapSignalR();
Next, we are going to add a SignalR hub. For this, add a new SignalR Hub Class (v2) item to your project.

This is the class that does the server side processing. In the example I attach to this article, I have the class defined as follows:
public class RealtimeNotifierHub : Hub
{
public int recordsToBeProcessed = 100000;
public void DoLongOperation()
{
for (int record = 0; record <= recordsToBeProcessed; record++)
{
if (ShouldNotifyClient(record))
{
Clients.Caller.sendMessage(string.Format
("Processing item {0} of {1}", record, recordsToBeProcessed));
Thread.Sleep(10);
}
}
}
private static bool ShouldNotifyClient(int record)
{
return record % 10 == 0;
}
}
At this point, our server is ready. We have a method called DoLongOperation
that is called from the client side. This method simulates a long running operation with 100.000 iterations and, at every 10 iterations, notifies the client of the current status. The Thread.Sleep
call was introduced to slow down the server, otherwise the for
loop runs so fast that the notification bar wouldn't be needed.
Next, we have to prepare the client. Create a new HTML page (or use the page where you want to show the notifications).
<!DOCTYPE html>
<html>
<head>
<title>Real-time notifier</title>
<script src="Scripts/jquery-1.10.2.min.js"></script>
-->
<script src="Scripts/noty/jquery.noty.js"></script>
<script src="Scripts/noty/layouts/top.js"></script>
<script src="Scripts/noty/themes/default.js"></script>
-->
<script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
<script src="/signalr/hubs"></script>
</head>
<body>
<div style="margin-top: 100px;">
-->
<input type="button" id="mybutton"
value="Call a time consuming server side operation" />
</div>
<script type="text/javascript">
$(function () {
// Initialize the connection to the server
var realtimeNotifier = $.connection.realtimeNotifierHub;
// Preparing a client side function
// called sendMessage that will be called from the server side
realtimeNotifier.client.sendMessage = function (message) {
showOrUpdateSuccessMessage(message, false);
};
// Establish the connection to the server. When done, sets the click of the button
$.connection.hub.start().done(function () {
$( // When the button is clicked,
// call the method DoLongOperation defined in the Hub
realtimeNotifier.server.doLongOperation();
});
});
});
</script>
<script type="text/javascript">
// Helper code that updates the noty notification bar
var n;
function showOrUpdateSuccessMessage(message, timeout) {
if (n == null) {
n = noty({ text: message, type: }
else {
n.setText(message);
}
}
</script>
</body>
</html>
The example above does several basic things:
- Establishes the connection from the client to the server
- Declares a client side function that will be called by the server
- Calls the server's method that performs the time consuming operation
- Updates the noty notification bar.
This is how it looks like when it's running:

Feel free to use this code. I have attached a Visual Studio 2013 solution that should just work.
If you have any questions, I'll be happy to answer them.
References