User Specific Notifications Using ASP.NET MVC & SignalR

In this post we are going to explore how to implement user based notification using ASP.Net MVC & SignalR.

In case if you are new to SignalR the please get some basics here:

Why SignalR?

SignalR provides “real-time” web functionality in our application using Javascript function call in client browser from server (Server Sent Events). It has several connection management like

  • connect/disconnect/reconnect events,
  • grouping connections,
  • authorization etc

Go to http://signalr.net for more.

We will focus on:

  1. Creating a new ASP.Net MVC Web Application
  2. Add SignalR
  3. Creating Hub
  4. Enable SignalR
  5. Database Modification
  6. Send specific user Notification using SignalR

Create ASP.Net MVC Web Application:

Open Visual Studio goto > File >New Project Choose ASP.Net MVC application

Choose a template in my case I have used MVC. Check Web API reference then hit ok button, that’s it. Build & run the application for first time.

Add SignalR:

Get it on NuGet! Right click the project > Manage NuGet package > Browse to install

Browse package then install to application, it’ll automatically done the rest.

OR Install using package manager console

Install-Package Microsoft.AspNet.SignalR

Creating Hub:

Add New Hub Class, Name it NotificationHub.cs

public class NotificationHub : Hub
{
    private static readonly ConcurrentDictionary Users =
        new ConcurrentDictionary(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()
        });

        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




Test SignalR

Run the application go to url modify by /signalr/hubs. This will show the magic SignalR JavaScript Library with current version like below screenshot.

In Browser Console it’ll show message about Hub Connection.

 

Database Modification:

Create new database then modify connection string in web.config file. Restore database from attached script file in App_Folder. We need to create a table to store Notifications.

CREATE TABLE [dbo].[Notification](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [Type] [int] NULL,
        [Details] [nvarchar](500) NULL,
        [Title] [nvarchar](50) NULL,
        [DetailsURL] [nvarchar](500) NULL,
        [SentTo] [nvarchar](50) NULL,
        [Date] [date] NULL,
        [IsRead] [bit] NULL,
        [IsDeleted] [bit] NULL,
        [IsReminder] [bit] NULL,
        [Code] [nvarchar](100) 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 view to send notification to specific user.

Index.cshtml:

Say Hello


Javascript:


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);

        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();
            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();
            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 Users =
        new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase);

    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();
                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();
                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()
        });

        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);
    }
}

Broadcast to Client

//Hub Method to Broadcast Notification to Specific User
context.Clients.Client(cid).broadcaastNotif(totalNotif);

//Server Sent Event
hub.client.broadcaastNotif = function (totalNotif) {
    setTotalNotification(totalNotif)
};

Call to Server

//Client Call to Server
hub.server.getNotification();

//Hub Method
public void GetNotification()
{

}

Finally Client Script:

Output:

Source Code: I’ve uploaded the full source code to download/clone , Hope this will help 🙂

Author:

Since March 2011, have 8+ years of professional experience on software development, currently working as Senior Software Engineer at s3 Innovate Pte Ltd.

15 thoughts on “User Specific Notifications Using ASP.NET MVC & SignalR”

Leave a Reply