User Specific Notifications Using ASP.NET MVC And SignalR

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.

  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 go to > File >New Project Choose ASP.Net MVC application.

ASP.NET

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.

SignalR

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

Browse package

OR Install using the package manager console

Install-Package Microsoft.AspNet.SignalR

 Microsoft

Creating HubAdd a new Hub Class, name it NotificationHub.cs.

 Hub Class

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.

Test SignalR

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

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

Output

Hope this will help.


Similar Articles