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
Set Enable Broker flag to true as below
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))
Right Click on your application and Select ManageNuGet Packages and Search SignalR in Online Section and select Microsoft Asp.net SignalR as below
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
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)
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.
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
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);
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.
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);
}
Go to Controller Folder and open Home Controller and add a action method as below
public ActionResult GetNotification()
{
return View();
}
Create a Empty view as below
Open newly creted view GetNotification.cshtml and add some Css in head section
<style type="text/css">.container {
background-color: #99CCFF;
border: thick solid #808080;
padding: 20px;
margin: 20px;
}</style>
Add SignalR Scripts in GetNotification.cshtml Page
var ew = $.connection.notificationHub;
ew.client.broadcastMessage = 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;
});
}
};
If we want to load all the messages at first time also we can write code like this
$.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
@{
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');
var ew = $.connection.notificationHub;
ew.client.broadcastMessage = 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;
});
}
};
$.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
Update some data in MessaageLog table
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.