In this post, we are going to explore how to implement user-based notification using ASP.NET MVC and SignalR. If you are new to SignalR, please get some basics here.
Why SignalR?
SignalR provides "real-time" web functionality in our application using Javascript function calls in the client browser from the server (Server-sent Events). It has several connections for management like
- connect/disconnect/reconnect events,
- grouping connections,
- authorization etc
Go to http://signalr.net for more.
We will focus on.
- Creating a new ASP.Net MVC Web Application
- Add SignalR
- Creating Hub
- Enable SignalR
- Database Modification
- Send specific user Notification using SignalR
Create ASP.Net MVC Web application
Open Visual Studio go to > File >New Project Choose ASP.Net MVC application.
Choose a template. In my case, I have used MVC. Check the Web API reference then hit the ok button, that’s it. Build & run the application for the first time.
Add SignalR
Get it on NuGet! Right-click the project > Manage NuGet package > Browse to install.
Browse the package then install to application, it’ll automatically do the rest.
OR Install using the package manager console
Install-Package Microsoft.AspNet.SignalR
Creating HubAdd a new Hub Class, name it NotificationHub.cs.
public class NotificationHub : Hub
{
private static readonly ConcurrentDictionary<string, UserHubModels> Users =
new ConcurrentDictionary<string, UserHubModels>(StringComparer.InvariantCultureIgnoreCase);
public override Task OnConnected()
{
string userName = Context.User.Identity.Name;
string connectionId = Context.ConnectionId;
var user = Users.GetOrAdd(userName, _ => new UserHubModels
{
UserName = userName,
ConnectionIds = new HashSet<string>()
});
lock (user.ConnectionIds)
{
user.ConnectionIds.Add(connectionId);
if (user.ConnectionIds.Count == 1)
{
Clients.Others.userConnected(userName);
}
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string userName = Context.User.Identity.Name;
string connectionId = Context.ConnectionId;
UserHubModels user;
Users.TryGetValue(userName, out user);
if (user != null)
{
lock (user.ConnectionIds)
{
user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId));
if (!user.ConnectionIds.Any())
{
UserHubModels removedUser;
Users.TryRemove(userName, out removedUser);
Clients.Others.userDisconnected(userName);
}
}
}
return base.OnDisconnected(stopCalled);
}
}
Enable SignalR Startup. cs
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(NotifSystem.Web.Startup))]
namespace NotifSystem.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Layout page
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(document).ready(function () {
var hub = $.connection.notificationHub;
$.connection.hub.start()
.done(function () {
console.log("Hub Connected!");
})
.fail(function () {
console.log("Could not Connect!");
});
});
</script>
Test SignalR
Run the application go to url modified by /signalr/hubs. This will show the magic SignalR JavaScript Library with the current version like the below screenshot.
In Browser Console it’ll show a message about Hub Connection.
Database Modification
Create a new database then modify the connection string in the web. config file. Restore the database from the attached script file in the App_Data folder.
We need to create a table to store Notifications.
CREATE TABLE [dbo].[Notification](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Type] [int] NULL,
NULL,
NULL,
NULL,
NULL,
[Date] [date] NULL,
[IsRead] [bit] NULL,
[IsDeleted] [bit] NULL,
[IsReminder] [bit] NULL,
NULL,
[NotificationType] [nvarchar](100) NULL,
CONSTRAINT [PK_Notification] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Send specific user Notification using SignalR
In this section, we will work with a view to send notifications to specific users.
Index. cshtml
<div class="col-md-12">
<h2>Say Hello</h2>
<div id="messages"></div>
<hr />
<div class="form-group">
<label for="email">User Email:</label>
<input type="email" class="form-control" id="toUser" value="" placeholder="[email protected]"/>
</div>
<div class="form-group">
<label for="pwd">Message:</label>
<input type="password" class="form-control" id="myMessage" value="" placeholder="message"/>
</div>
<button type="submit" class="btn btn-default" id="submit">Submit</button>
</div>
Javascript
<script type="text/javascript">
$("#submit").click(function (e) {
e.preventDefault();
var message = $("#myMessage").val();
var sendtouser = $("#toUser").val();
var Notification = { UserID: sendtouser, Message: message };
$.ajax({
type: "POST",
url: "/api/Values/SendNotification",
data: JSON.stringify(Notification),
contentType: 'application/json; charset=utf-8',
success: function (data) {
//reset field
$("#myMessage").val("");
},
error: function () {
alert("Error occurred!!");
}
});
});
</script>
API
public class ValuesController : ApiController
{
private NotifEntities context = new NotifEntities();
[HttpPost]
public HttpResponseMessage SendNotification(NotifModels obj)
{
NotificationHub objNotifHub = new NotificationHub();
Notification objNotif = new Notification();
objNotif.SentTo = obj.UserID;
context.Configuration.ProxyCreationEnabled = false;
context.Notifications.Add(objNotif);
context.SaveChanges();
objNotifHub.SendNotification(objNotif.SentTo);
//var query = (from t in context.Notifications
// select t).ToList();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
Modify Hub
We need to add additional methods in our Hub class.
private NotifEntities context = new NotifEntities();
// Logged Use Call
public void GetNotification()
{
try
{
string loggedUser = Context.User.Identity.Name;
// Get TotalNotification
string totalNotif = LoadNotifData(loggedUser);
// Send To
UserHubModels receiver;
if (Users.TryGetValue(loggedUser, out receiver))
{
var cid = receiver.ConnectionIds.FirstOrDefault();
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.Client(cid).broadcaastNotif(totalNotif);
}
}
catch (Exception ex)
{
ex.ToString();
}
}
// Specific User Call
public void SendNotification(string SentTo)
{
try
{
// Get TotalNotification
string totalNotif = LoadNotifData(SentTo);
// Send To
UserHubModels receiver;
if (Users.TryGetValue(SentTo, out receiver))
{
var cid = receiver.ConnectionIds.FirstOrDefault();
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.Client(cid).broadcaastNotif(totalNotif);
}
}
catch (Exception ex)
{
ex.ToString();
}
}
private string LoadNotifData(string userId)
{
int total = 0;
var query = (from t in context.Notifications
where t.SentTo == userId
select t)
.ToList();
total = query.Count;
return total.ToString();
}
Finally the Hub
public class NotificationHub : Hub
{
private static readonly ConcurrentDictionary<string, UserHubModels> Users =
new ConcurrentDictionary<string, UserHubModels>(StringComparer.InvariantCultureIgnoreCase);
private NotifEntities context = new NotifEntities();
// Logged User Call
public void GetNotification()
{
try
{
string loggedUser = Context.User.Identity.Name;
// Get TotalNotification
string totalNotif = LoadNotifData(loggedUser);
// Send To
UserHubModels receiver;
if (Users.TryGetValue(loggedUser, out receiver))
{
var cid = receiver.ConnectionIds.FirstOrDefault();
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.Client(cid).broadcaastNotif(totalNotif);
}
}
catch (Exception ex)
{
ex.ToString();
}
}
// Specific User Call
public void SendNotification(string SentTo)
{
try
{
// Get TotalNotification
string totalNotif = LoadNotifData(SentTo);
// Send To
UserHubModels receiver;
if (Users.TryGetValue(SentTo, out receiver))
{
var cid = receiver.ConnectionIds.FirstOrDefault();
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.Client(cid).broadcaastNotif(totalNotif);
}
}
catch (Exception ex)
{
ex.ToString();
}
}
private string LoadNotifData(string userId)
{
int total = 0;
var query = (from t in context.Notifications
where t.SentTo == userId
select t)
.ToList();
total = query.Count;
return total.ToString();
}
public override Task OnConnected()
{
string userName = Context.User.Identity.Name;
string connectionId = Context.ConnectionId;
var user = Users.GetOrAdd(userName, _ => new UserHubModels
{
UserName = userName,
ConnectionIds = new HashSet<string>()
});
lock (user.ConnectionIds)
{
user.ConnectionIds.Add(connectionId);
if (user.ConnectionIds.Count == 1)
{
Clients.Others.userConnected(userName);
}
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string userName = Context.User.Identity.Name;
string connectionId = Context.ConnectionId;
UserHubModels user;
Users.TryGetValue(userName, out user);
if (user != null)
{
lock (user.ConnectionIds)
{
user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId));
if (!user.ConnectionIds.Any())
{
UserHubModels removedUser;
Users.TryRemove(userName, out removedUser);
Clients.Others.userDisconnected(userName);
}
}
}
return base.OnDisconnected(stopCalled);
}
}
Output
Hope this will help.