Click here to Skip to main content
15,867,704 members
Articles / Web Development / HTML

SignalR - Message Watch Application in C# with Database Notifications

Rate me:
Please Sign up or sign in to vote.
4.81/5 (40 votes)
19 Sep 2015CPOL2 min read 47.6K   2.7K   58   15
Signal R Message Logging Application

Introduction

SignalR is a library which provide the process of adding real-time updates in web application. In Real-time web functionality Server automatically sends response to connected clients rather than having the server wait for a client to request new data.

Background

Message Watch is a web application which takes data from a database and updates data in real time to clients.In this web app, Different application logs message in a database and through database notification and SignalR we will update those messages to users at real time.

Using the code

I would like to start with the database(In Step 1 and Step 2) and then we will jump to our C# code.

Step 1 (Set up Database) : Create a database named SignalRDatabase and Create below tables

a. Application

b. logLevel

c. MessageLog

d. OperationCode

Image 1

Set Enable Broker flag to true as below 

Image 2

Step 2 (Set up Application) : Open Visual Studio(I am using Visual Studio 2013) and Then Click on Web and then select Asp.net web application and then Select MVC(give a name to application(MessageWatch in my case))

Image 3

Image 4

Right Click on your application and Select ManageNuGet Packages and Search SignalR in Online Section and select Microsoft Asp.net SignalR as below

Image 5

 

After installation, check Reference folder, There will be some references related to SignalR.

Step 3 (Its Code Time) : 

Go to Startup.cs and Map SignalR as below

C#
using Microsoft.Owin;
using Owin;

[assembly: OwinStartupAttribute(typeof(MessageWatch.Startup))]
namespace MessageWatch
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

Create a class named MessageLog.cs (Add all the properties which you want to update to clients)

C#
public class MessageLog
{
    public string Name { get; set; }

    public int EventID { get; set; }

    public string LogLevelName { get; set; }

    public string OperationCodeName { get; set; }

    public string ServerName { get; set; }

    public string ComponentName { get; set; }

    public string SubComponentName { get; set; }
}

 

Right Click on the solution and add a SignalR Hub Class(NotificationHub.cs in my case) as below. Responsibility of this function is to send updates to clients.

Image 6

Add a method named GetAllMessagesLog() in NotificatiobHub.cs. It will pull all the required information from the database and will call send() function.

In This function we are setting up database dependency and fetching all the records from the database

C#
public IEnumerable<MessageLog> GetAllMessagesLog()
   {
       string conStr = ConfigurationManager.ConnectionStrings["SignalRDB"].ConnectionString;
       SqlConnection connection = new SqlConnection(conStr);

       string query = "SELECT Message,EventID,LL.Name as LogLevelID,OC.Name as OperationCodeID,ML.ServerName,ML.ComponentName,ML.SubComponentName FROM [dbo].[MessageLog] ML inner join [dbo].[LogLevel] LL on ML.LogLevelID = LL.ID inner join [dbo].[OperationCode] OC on ML.OperationCodeID = OC.ID";
       SqlDependency.Start(conStr);
       SqlCommand command = new SqlCommand(query, connection);
       SqlDependency dependency = new SqlDependency(command);

       //If Something will change in database and it will call dependency_OnChange method.
       dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
       connection.Open();
       SqlDataAdapter da = new SqlDataAdapter(command);
       DataTable dt = new DataTable();
       da.Fill(dt);

       List<MessageLog> messageList = new List<MessageLog>();
       for (int i = 0; i < dt.Rows.Count; i++)
       {
           MessageLog ml = new MessageLog();
           ml.Name = dt.Rows[i]["Message"].ToString();
           ml.EventID = Convert.ToInt32(dt.Rows[i]["EventID"].ToString());
           ml.LogLevelName = dt.Rows[i]["LogLevelID"].ToString();
           ml.OperationCodeName = dt.Rows[i]["OperationCodeID"].ToString();
           ml.ServerName = dt.Rows[i]["ServerName"].ToString();
           ml.ComponentName = dt.Rows[i]["ComponentName"].ToString();
           ml.SubComponentName = dt.Rows[i]["SubComponentName"].ToString();
           messageList.Add(ml);
       }
       return messageList;

   }

Add Two more methods which will get called when something will change in database.

C#
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
   {
       if (e.Type == SqlNotificationType.Change)
       {

           SendNotifications();
       }
   }
   private void SendNotifications()
   {
       IEnumerable<MessageLog> messageList = GetAllMessagesLog();

       IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
       context.Clients.All.broadcastMessage(messageList);//Will update all the clients with message log.

   }

Go to Controller Folder and open Home Controller and add a action method as below

C#
public ActionResult GetNotification()
    {
        return View();
    }

Create a Empty view as below

Image 7

Open newly creted view GetNotification.cshtml and add some Css in head section

C#
<style type="text/css">.container {
            background-color: #99CCFF;
            border: thick solid #808080;
            padding: 20px;
            margin: 20px;
        }</style>
<style type="text/css">
body {
       font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
       font-size: 16px;
        
     }

#messageTable table {
            border-collapse: collapse;
                    }

#messageTable table th, #messageTable table td {
                padding: 2px 6px;
            }

#messageTable table td {
                text-align: right;
            }

#messageTable .loading td {
            text-align: left;
        }
</style>

Add SignalR Scripts in GetNotification.cshtml Page

C#
 var ew = $.connection.notificationHub;
            //This method will fill all the Messages in case of any database change.
            //Server side code will call this function to update information client.
            ew.client.broadcastMessage = function (messageLogs) {
                // Html encode display name and message.
                var i = 0;
                if ($('#hdnValue').val() == 1) {
                    $messageTableBody.empty();

                    $.each(messageLogs, function () {
                        var encodedName = messageLogs[i].Name;
                        var encodedEvent = messageLogs[i].EventID;
                        var encodedLogLevel = messageLogs[i].LogLevelName;
                        var encodedOCode = messageLogs[i].OperationCodeName;
                        var encodedServerName = messageLogs[i].ServerName;
                        var encodedCompName = messageLogs[i].ComponentName;
                        var encodedSubCompName = messageLogs[i].SubComponentName;
                        if (encodedLogLevel == "Fatal") {
                            $messageTableBody.append('<tr style="background-color:Red"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        if (encodedLogLevel == "Warning") {
                            $messageTableBody.append('<tr style="background-color:Yellow"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        else {
                            $messageTableBody.append('<tr><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        i = i + 1;
                    });
                }
            };

If we want to load all the messages at first time also we can write code like this

C#
$.connection.hub.start().done(function () {

            ew.server.getAllMessagesLog().done(function (messageLogs) {
                var i = 0;
                if ($('#hdnValue').val() == 1) {
                    $messageTableBody.empty();
                    $.each(messageLogs, function () {
                        var encodedName = messageLogs[i].Name;
                        var encodedEvent = messageLogs[i].EventID;
                        var encodedLogLevel = messageLogs[i].LogLevelName;
                        var encodedOCode = messageLogs[i].OperationCodeName;
                        var encodedServerName = messageLogs[i].ServerName;
                        var encodedCompName = messageLogs[i].ComponentName;
                        var encodedSubCompName = messageLogs[i].SubComponentName;
                        if (encodedLogLevel == "Fatal") {
                            $messageTableBody.append('<tr style="background-color:Red"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        if (encodedLogLevel == "Warning") {
                            $messageTableBody.append('<tr style="background-color:Yellow"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        else {
                            $messageTableBody.append('<tr><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        i = i + 1;
                    });
                }
            });
        });

GetNotification.cs : Complete Code of this page is

Razor
@{
    Layout = null;
}

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Message Watch</title>
    <style type="text/css">
        .container {
            background-color: #99CCFF;
            border: thick solid #808080;
            padding: 20px;
            margin: 20px;
        }
    </style>
    <style>
        body {
            font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
            font-size: 16px;
        }

        #messageTable table {
            border-collapse: collapse;
        }

            #messageTable table th, #messageTable table td {
                padding: 2px 6px;
            }

            #messageTable table td {
                text-align: right;
            }

        #messageTable .loading td {
            text-align: left;
        }
    </style>
</head>
<body>
    <div class="container">
        <div id="messageTable">
            <table border="1">
                <thead>
                    <tr><th>Message< /th><th>EventID</th><th>Component Name</th><th>OperationCodeName</th><th>ServerName</th><th>ComponentName</th><th>SubComponentName</th></tr>
                </thead>
                <tbody>
                  
                </tbody>
            </table>
            <input type="button" id="btnPause" name="Pause" value="Pause" />
            <input type="button" id="btnResume" name="Resume" value="Resume" />
            <input id="hdnValue" type="hidden" value="1" />

        </div>
    </div>
   
    <script src="~/Scripts/jquery-1.10.2.js"></script>
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
    <script src="~/signalr/hubs"></script>
    <script type="text/javascript">
        $(function () {
            $('#btnPause').click(function () {
                $('#hdnValue').val(0);
            });
            $('#btnResume').click(function () {
                $('#hdnValue').val(1);
            });
            var $messageTable = $('#messageTable');
            var $messageTableBody = $messageTable.find('tbody');
            // Declare a proxy to reference the hub.
            var ew = $.connection.notificationHub;
            //This method will fill all the Messages in case of any database change.
            ew.client.broadcastMessage = function (messageLogs) {
                // Html encode display name and message.
                var i = 0;
                if ($('#hdnValue').val() == 1) {
                    $messageTableBody.empty();

                    $.each(messageLogs, function () {
                        var encodedName = messageLogs[i].Name;
                        var encodedEvent = messageLogs[i].EventID;
                        var encodedLogLevel = messageLogs[i].LogLevelName;
                        var encodedOCode = messageLogs[i].OperationCodeName;
                        var encodedServerName = messageLogs[i].ServerName;
                        var encodedCompName = messageLogs[i].ComponentName;
                        var encodedSubCompName = messageLogs[i].SubComponentName;
                        if (encodedLogLevel == "Fatal") {
                            $messageTableBody.append('<tr style="background-color:Red"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        if (encodedLogLevel == "Warning") {
                            $messageTableBody.append('<tr style="background-color:Yellow"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        else {
                            $messageTableBody.append('<tr><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                        }
                        i = i + 1;
                    });
                }
            };

            //This method will fill all the Messages initially
            $.connection.hub.start().done(function () {
             
                ew.server.getAllMessagesLog().done(function (messageLogs) {
                    var i = 0;
                    if ($('#hdnValue').val() == 1) {
                        $messageTableBody.empty();
                        $.each(messageLogs, function () {
                            var encodedName = messageLogs[i].Name;
                            var encodedEvent = messageLogs[i].EventID;
                            var encodedLogLevel = messageLogs[i].LogLevelName;
                            var encodedOCode = messageLogs[i].OperationCodeName;
                            var encodedServerName = messageLogs[i].ServerName;
                            var encodedCompName = messageLogs[i].ComponentName;
                            var encodedSubCompName = messageLogs[i].SubComponentName;
                            if (encodedLogLevel == "Fatal") {
                                $messageTableBody.append('<tr style="background-color:Red"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                            }
                            if (encodedLogLevel == "Warning") {
                                $messageTableBody.append('<tr style="background-color:Yellow"><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                            }
                            else {
                                $messageTableBody.append('<tr><td>' + encodedName + '</td><td>' + encodedEvent + '</td><td>' + encodedLogLevel + '</td><td>' + encodedOCode + '</td><td>' + encodedServerName + '</td><td>' + encodedCompName + '</td><td>' + encodedSubCompName + '</td></tr>');
                            }
                            i = i + 1;
                        });
                    }
                });
            });
        });

    </script>
</body>

Run the application and you will find below result

Image 8

Update some data in MessaageLog table

C#
Insert into MessageLog values(4,102,3,2,13,3467,'Test123','DocClass.dll','GetData()','345','User is unable to get data','Sample Stack Trace',getdate())

All the client will updated real time with updated/Inserted data.

There are two buttons named Pause and Resume to pause and resume real time data.

 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
Hi Myself Vijay having around 7 years of experience on Microsoft Technologies.

Comments and Discussions

 
QuestionWindows Forms - WPF Pin
kiquenet.com6-Nov-20 11:32
professionalkiquenet.com6-Nov-20 11:32 
QuestionMy vote of 5 Pin
ThomaLuke17-Nov-17 3:48
professionalThomaLuke17-Nov-17 3:48 
GeneralMy vote of 5 Pin
Moshe Ventura18-Nov-15 0:11
Moshe Ventura18-Nov-15 0:11 
GeneralMy vote of 5 Pin
Rupesh Telang19-Oct-15 21:13
Rupesh Telang19-Oct-15 21:13 
GeneralMy vote of 5 Pin
Ehsan Sajjad12-Oct-15 5:11
professionalEhsan Sajjad12-Oct-15 5:11 
QuestionMy vote is 5 Pin
SunilGupta04851-Oct-15 7:00
professionalSunilGupta04851-Oct-15 7:00 
GeneralMy voteof 5! Pin
Greg Good24-Sep-15 9:59
Greg Good24-Sep-15 9:59 
QuestionConfiguration? Pin
c-sharp-dork21-Sep-15 5:57
c-sharp-dork21-Sep-15 5:57 
QuestionMy Vote of 5 !!! Pin
Ankur11june20-Sep-15 21:42
Ankur11june20-Sep-15 21:42 
QuestionA small question Pin
Suvabrata Roy20-Sep-15 21:21
professionalSuvabrata Roy20-Sep-15 21:21 
AnswerRe: A small question Pin
VijayRana20-Sep-15 21:26
professionalVijayRana20-Sep-15 21:26 
GeneralRe: A small question Pin
Suvabrata Roy20-Sep-15 21:27
professionalSuvabrata Roy20-Sep-15 21:27 
Question.. Pin
Member 1156416020-Sep-15 5:40
Member 1156416020-Sep-15 5:40 
GeneralMy vote of 5 Pin
Santhakumar M20-Sep-15 5:27
professionalSanthakumar M20-Sep-15 5:27 
QuestionVery nice! Pin
2374119-Sep-15 13:23
2374119-Sep-15 13:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.