In today's guide, I'm going to go over a demo project I've created that implements real-time notifications within Episerver. In this demo, notifications can be updated in real-time either through a 'Content Page Type' being published within Episerver, or, whenever a scheduled task called 'Notification Scheduled Task' runs.
When either of these things occurs, the user will instantly be made aware of the notification on the homepage, without them having to refresh the page. The system will need to have three main functions:
Get a list of current notifications
Add new notifications and broadcast to all clients when this happens
Mark a notification as read from the client side As there is a lot of code to set this code, I'm only going to cover the more important aspects.
The real-time messaging works through a library called SignalR. I won't cover how to install SignalR here as I've previously gone over it (see above). The main class that hangs everything together is the 'Hub'. A hub class is a special SignalR class you need to inherit from. My Hub looks like this:
The hub's main purpose is to manage the messaging. How you architecture this file is up to you. I've followed an approach based on an MS tutorial, by implementing a singleton class called 'NotificationCentre' to handle the notifications business logic. The 'NotificationCentre' does the actual managing of Notification objects. In this example, it's all done in-memory, but in the reality, this data may live in a No-SQL, or SQL database.
A singleton is useful in this situation as it stops duplicate actions being performed. For example, if someone had the scheduled jobs running twice, you could possibly end up with two different notifications for the same action... which I definitely don't want to happen. The code above is pretty self-explanatory, you inherit from 'Hub' and then implement any methods that your custom logic needs. The NotificationCentre code is a little bit more interesting:
Most of the stuff in here is custom logic that I've architected in a style I like. I define a List of Notification objects, this will be used to store the notifications. I've implemented a private constructor to ensure it's a singleton. I've added three methods, AddNewNotification, DeleteAllNotifications and GetAllSystemNotifications that does my custom business logic.
The only real SignalR code in this class is BroadcastUpdate. In here I'm passing in my list of Notification objects, into Clients.All.updateNotifications. Clients.All is a signalR thing, updateNotifications is the name of an event that I defined.
You can call the methods you define whatever you want to, however, they have to match up with the names you'll use in the HTML. In my HTML file (which I'll cover below), I create a listener that is attached to 'updateNotifications'. So whenever I call BroadcastUpdate any clients that have registered to that method will get triggered. The HTML to hook this all up, is below:
To help make it easier to visualise what the code looks like, see below:
The page has two buttons, one to mark a notification as read and the other to delete the message. Pressing either of these buttons will trigger a call to the notification hub class. The notification hub will then call an instance of the singleton NotificationCentre class and perform the relevant action. As mentioned above, each method that will change the List of notification will eventually call BroadcastUpdate() which will trigger the 'updateNotifications', perform a system broadcast so the clients can refresh their views accordingly.
On the first line, we create a reference to the hub using .connection. The name of the hub should be the same as the class name you created earlier (it's the class that inherits from 'Hub'). Remember, the first letter of the method needs to be lowercase, otherwise the JS will complain that it can't find it!
The code within the function updates the HTML when a broadcast occurs. The code deletes all the current notifications out of the notificationBoard div and then loop through each notification pushed from the server and write it to the HTML. Another thing that is useful to notice is the 'notificationHub.client' call.
When you use SignalR you have 'client' and 'server' calls. The client is the way that you set up a listener. Notice how updateNotifications is the same name as the method I make in the NotificationCentre -> BroadcastUpdate() method. Using the same name is the way it hooks the process up. On your server-side code, you write a Clients.All. to do the broadcast. To push to the hub, you use a 'client' call, like so:
This code is a little easier to understand. You're pushing, so you make a 'server' call. The method name on your server call should relate to the method name within your hub class. Again, make sure the method name starts with a lowercase letter, otherwise expect to see a JS exception within the console.
These two different types of call were the main thing that confused me when I started with SignalR. When you want to push to the server, you use the 'server' call and the method name of your C# hub code.
When you want to create a listener, you need the SignalR method using the 'client' call. In my case this is 'updateNotifications'. This name not translated to a C# class name, this is simply a method you register with SignalR. You can create as many of these methods as you like and call them whatever you want.
To finish off this tutorial, I'll quickly add the code examples for the page publishing event listener and the scheduled task code. If this code is unfamiliar with you then I suggest that you read, Episerver Scheduled Tasks and How to Hook Into The Episerver CMS Events Pipeline .
Real-Time Notification takeaway
Real-time notifications are pretty easy when you implement SignalR. It comes with a pretty good server-side and client-side framework that takes the complexities of web sockets and managing to message away from you and allows you to focus on your code
The project is available to download from my Github account, here.
For more information, I suggest you look atHow To Install SignalR Within Episerver.