Push notification with SignalR across session state
Using SignalR is pretty simple for implemeting push notification service. One thing to keep in mind is: SignalR connections (including the connection underlying all Hub operations for a client) do not support Session state. So it's not possible to access session data from signalr hub.
Now let's assume one scenario, where we need to perform a database operation on postback (e.g. post action in an mvc controller) based on a user action and after that we need to redirect to a listing/default page. But we want to let user know if the operation succeeded or not.
Here is a Hub implemented to send client notification:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NotificationHub : Hub | |
{ | |
private readonly INotifier _notificationService; | |
public NotificationHub(INotifier notificationService) | |
{ | |
_notificationService = notificationService; | |
} | |
public void Notify() | |
{ | |
_notificationService.ShowNotification(); | |
} | |
} |
Here is how it is initialized:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[assembly: OwinStartup(typeof(Client.Notification.HubInitializer))] | |
namespace Client.Notification | |
{ | |
public class HubInitializer | |
{ | |
public void Configuration(IAppBuilder app) | |
{ | |
GlobalHost.DependencyResolver.Register( | |
typeof(NotificationHub), | |
() => new NotificationHub(new ClientNotification())); | |
app.MapSignalR(); | |
} | |
} | |
} |
And here is the client side script:
The final thing is to invoke the client side
showNotification() function when the redirected view page is loaded.
But problem is: the calling page and the redirected page are two different views and the session state is changed in the meantime. After redirection client hub in the calling page is closed and we must init the hub again before we can send notification. So, even if we re-open the hub on page load we must know few notification data (e.g. message) that should actually come from previous page.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var notificationHub = $.connection.notificationHub; | |
notificationHub.client.showNotification = function (message, status) { | |
$(".notification").removeClass("alert-danger").addClass("alert-info"); | |
if (status && status == "Failure") { | |
$(".notification").removeClass("alert-info").addClass("alert-danger"); | |
} | |
$(".notification").html(message); | |
$(".notification").fadeIn('slow').delay(2000).fadeOut("slow"); | |
}; | |
$.connection.hub.start(); |
But problem is: the calling page and the redirected page are two different views and the session state is changed in the meantime. After redirection client hub in the calling page is closed and we must init the hub again before we can send notification. So, even if we re-open the hub on page load we must know few notification data (e.g. message) that should actually come from previous page.
Since signalr doesn't allow session data, these piece of data must persist somewhere else while the state change is taking place.
Here is how it is done using static variable:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ClientNotification : INotifier | |
{ | |
private static PushNotification _notification; | |
public void SendMessage(string message, NotificationStatus status) | |
{ | |
GlobalHost | |
.ConnectionManager | |
.GetHubContext<NotificationHub>().Clients.All.showNotification(message, status.ToString()); | |
} | |
public void PushNotification(string message, NotificationStatus status) | |
{ | |
var notification = new PushNotification() { Message = message, Status = status}; | |
_notification = notification; | |
} | |
public void ShowNotification() | |
{ | |
if (_notification != null) | |
{ | |
SendMessage(_notification.Message, _notification.Status); | |
_notification = null; | |
} | |
} | |
} |
So remaining is to push the notification on completion of database operation and then show the push notification on page load.
Pushing notification to hub:
Displaying push notification:
That's all! Now the push notification will be displyed when redirected page is loaded.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[HttpPost] | |
public ActionResult Save(Employee employee) | |
{ | |
string successMessage = "Employee information updated successfully."; | |
_employeeService.Update(employee); | |
_notificationService.PushNotification(successMessage, NotificationStatus.Success); | |
return RedirectToAction("Index", "Home"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var notificationHub = $.connection.notificationHub; | |
$.connection.hub.start().done(function () { | |
notificationHub.server.notify(); | |
}); |
Comments
Post a Comment