Database Change Notifications in ASP.NET using SignalR and SqlDependency - c#

I'm a serious MVC & SignalR newbie!
I found this tutorial online that shows how to use Database Change notifications and display the data in an MVC app. The issue I'm having is replicating the MVC side of things. I've managed I think to work through the tutorial correctly and spin up the application, however, I'm getting an Undefined connection and the app bombs out.
Does anyone know where I might find the associated source files for this example, or has anyone managed to successfully implement this and can shed some light on the configuration of this from an MVC point of view.
Thanks!

To display real time updates from the SQL Server by using SignalR and SQL Dependency I've done these steps:
Step 1: Enable Service Broker on the database
The following is the query that need to enable the service broker
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
Step 2: Enable SQL Dependency
//Start SqlDependency with application initialization
SqlDependency.Start(connString);
Step 3: Create the hub Class
public class MessagesHub : Hub
{
private static string conString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
public void Hello()
{
Clients.All.hello();
}
[HubMethodName("sendMessages")]
public static void SendMessages()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>();
context.Clients.All.updateMessages();
}
}
Step 4: Get the Data from the Repository
Create MessagesRepository to get the messages from the database when data is updated.
public class MessagesRepository
{
readonly string _connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
public IEnumerable<Messages> GetAllMessages()
{
var messages = new List<Messages>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [MessageID], [Message], [EmptyMessage], [Date] FROM [dbo].[Messages]", connection))
{
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new Messages { MessageID = (int)reader["MessageID"], Message = (string)reader["Message"], EmptyMessage = reader["EmptyMessage"] != DBNull.Value ? (string) reader["EmptyMessage"] : "", MessageDate = Convert.ToDateTime(reader["Date"]) });
}
}
}
return messages;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
MessagesHub.SendMessages();
}
}
}
Step 5: Register SignalR at startup class
app.MapSignalR();
Step 6: then use the method to show real time at your view
<script src="/Scripts/jquery.signalR-2.1.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.messagesHub;
//debugger;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function () {
getAllMessages()
};
// Start the connection.
$.connection.hub.start().done(function () {
alert("connection started")
getAllMessages();
}).fail(function (e) {
alert(e);
});
});
function getAllMessages()
{
var tbl = $('#messagesTable');
$.ajax({
url: '/home/GetMessages',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
</script>
Hope this helps :)

You should provide your code, it's easier to figure out what the problem is that way.
From what you have mentioned, I can think of only two things.
1) The tutorial you used, it's using SignalR 1.0. If you are using SignalR 2.0, you should not follow the tutorial exactly.
A few things changed in SignalR 2.0, you can read about it using below link:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/upgrading-signalr-1x-projects-to-20
2) If you are using SignalR 1.0, make sure you include the "jquery" file correctly, and if it's correct, try to change the hub proxy path like below:
<script src="/signalr/Hubs"></script>
Hope it helps

Related

SqlDependency OnChange event firing multiple times

I'm have a comment system on a project where you can view different pages and they have their own comments. I'm trying to use signalR with SqlDependency to automatically pull new comments on the page.
My problem is that if multiple people have a connection open with SqlDependency the number of "onchange" events called from the sqlnotification start getting multiplied. Instead of the onchange even getting called once it will be called multiple times for each user. (Even if they are not viewing the same comments page)
I've pretty much exhausted all searching here with the most common response being that I need to unsubscribe the event when it's called like this:
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
if (e.Info.ToString().ToLower().Trim() == "insert")
{
MyHub.Show();
}
}
This didn't seem to change anything for me so I'm lost on what the best way to prevent this would be.
GetData method:
[WebMethod]
public IEnumerable<DUpdates> GetData(string dnum, string date)
{
if (Common.IsValidInt32(dnum)) //just a function that check for valid int
{
using (var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["SConnectionString"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [Comment] FROM dbo.DUpdates WHERE (DID = " + dnum + ") AND (Deleted = 0) AND CreateDate > #Date ORDER BY CreateDate DESC", connection))
{
command.Parameters.Add("#Date", SqlDbType.DateTime);
command.Parameters["#Date"].Value = date;
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
return reader.Cast<IDataRecord>().Select(x => new DUpdates()
{
comment = x.GetString(0)
}).ToList();
}
}
}
}
JS stuff:
$(function() {
var job = $.connection.myHub;
job.client.displayStatus = function () {
getData();
};
$.connection.hub.start();
getData();
});
function getData() {
var params = (new URL(document.location)).searchParams;
var id = params.get("id");
var dt = $("#accessdate").val();
$.ajax({
url: 'dupdatesservice.asmx/GetData',
data: JSON.stringify({dnum: id, date: dt}),
contentType: "application/json; charset=utf-8",
dataType: "json",
type: "POST",
success: function (data) {
if (data.d.length > 0) {
$("#testdiv").prepend(data.d[0].comment);
}
}
});
}
Can provide other code if needed.
The issue here is that I was creating a new SqlDependency for each user that was on a page (or same user with multiple windows). So if 2 windows were open for a page, then it would check for notifications twice and send the response twice if there was something new. Because of the ajax request, now all of those SqlDependencies were doubled so I'd get 4 responses the next time, then 8 and so on.
What I decided to do instead was to essentially change my pages to private chats using signalr and just ditched the SqlDependency stuff. So now if a user goes to one page, they are connected with anyone else on the page and anytime someone submits a "comment" it also gets sent to other people viewing the page.

Why SignalR change function hitting many many times?

I try to develop an asp.net mvc application and also trying to use signalr. The problem is that i have two tables that control user notificitaions in project. I have a Notification table and also NotificationUser table which is many to many table of notification and user tables. I am trying achive that if a user create a notification to another user in system I try to show a pop-up that confirm user with a simple message like 'Hey!New notification received'. The problem is javascript change function of signalr hitting so many times.All step i used in signalR listed below
the stored procedure
ALTER PROCEDURE [dbo].[GetNotifications]
#userid int
AS
BEGIN
select n.Ntf_Title,Ntf_Description,n.Ntf_Date from dbo.SysNotifications n INNER JOIN dbo.SysNotificationUser u on n.Ntf_ID =u.NtU_NtfID where NtU_UserID=#userid AND NtU_IsRead=0
END
The Hub
[HubName("signalRHub")]
public class NtfHub : Hub
{
[HubMethodName("notifyChanges")]
public static void NotifyChanges()
{
var context = GlobalHost.ConnectionManager.GetHubContext<NtfHub>();
context.Clients.All.notifyChanges();
}
}
The StartUp Class
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
The Partial View
[HttpGet]
public ActionResult GetNtf()
{
//NtfRepo rp = new NtfRepo(this.HttpContext);
string connectionString = ConfigurationManager.ConnectionStrings["conn"].ConnectionString;
int userid =id;
using (SqlConnection sqlcon = new SqlConnection(connectionString))
{
using (SqlCommand sqlcom = new SqlCommand("[GetNotifications]", sqlcon))
{
sqlcon.Open();
sqlcom.CommandType = CommandType.StoredProcedure;
sqlcom.Parameters.AddWithValue("#userid", userid);
sqlcom.Notification = null;
SqlDependency dependancy = new SqlDependency(sqlcom);
dependancy.OnChange += dependancy_OnChange;
var reader = sqlcom.ExecuteReader();
var ntf= reader.Cast<IDataRecord>()
.Select(e => new PopulateNtfBar()
{
Title = e.GetString(0),
Description = e.GetString(1),
TimeDiff = FindDifferenceTime(e.GetDateTime(2))
}).ToList();
return PartialView("~/Views/Shared/CheckNotification.cshtml", ntf);
}
}
}
At Last,The Script
$(function () {
var notification = $.connection.signalRHub;
// Create a function that the hub can call to broadcast messages.
notification.client.notifyChanges = function () {
getData();
toastr.warning("Hey,You have Ntf");
};
// Start the connection.
$.connection.hub.start().done(function () {
getData();
}).fail(function (e) {
});
});
function getData() {
var tbl = $("#header_notification_bar")
$.ajax({
url: '#Url.Action("GetNtf","Home")',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
notification.client.notifyChanges hitting so many times if a user create a notification.Where is the problem? Any idea? i cannot optimize it
EDIT 1
I am calling NtfHub.NotifyChanges in controller.
void dependancy_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
NtfHub.NotifyChanges();
}
}
Although I think that SqlDependency is wrong approach for this feature, you could try to solve this specific problem this way:
Add parameter "subscribeToNotifications" to your controller action
public ActionResult GetNtf(bool subscribeToNotifications)
Create SqlDependency only if it is True.
Then subscribe to notifications only on hub started (this will prevent creation of multiple SqlDependencies for same user):
$(function () {
var notification = $.connection.signalRHub;
// Create a function that the hub can call to broadcast messages.
notification.client.notifyChanges = function () {
getData(false);
toastr.warning("Hey,You have Ntf");
};
// Start the connection.
$.connection.hub.start().done(function () {
getData(true);
}).fail(function (e) {
});
});
function getData(subscribeToNotifications) {
var tbl = $("#header_notification_bar")
$.ajax({
url: '#Url.Action("GetNtf","Home")' + '?subscribeToNotifications=' + subscribeToNotifications,
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
But be aware that every page refresh will still create new listener without managing subscriptions on server side.
Option 2 is to create single SqlDependency (on server app start) omitting userId parameter - anyway you are sending notification to all users no matter which one got the message.
Option 3 - the real solution is get rid of SqlDependency at all and send notification only to specific user (recipient of the message)
The reason is that you are not unsubscribing from the dependancy_OnChange event, a sqldependency trigger is a one shot execution, so you have to subscribe to the new one every time it fires, what you are not doing is unsubscribing from the previous event handler, so when you subscribe to the new one, you now have multiple handlers for the same trigger.
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
}

How to invoke NodeJs code from C# MVC?

I used mqtt code with nodeJs which runs in console.
But I want to use this nodeJs Mqtt code in c# MVC.
How can I achieve it?
My NodeJs Code as follows,
Server.js
var mqtt = require('mqtt');
var sys = require("sys");
var stdin = process.openStdin();
var client = mqtt.connect('http://broker.mqttdashboard.com');
client.on('connect', function () {
console.log("Chat Application");
stdin.addListener("data", function(d) {
client.publish('presence',d.toString());
});
client.subscribe('msg');
client.on('message', function (topic, message) {
console.log('User2:',message.toString());
});
});
Client.js
var mqtt = require('mqtt');
var client = mqtt.connect('http://broker.mqttdashboard.com');
var sys = require("sys");
var stdin = process.openStdin();
client.on('connect', function () {
console.log("Chat Application");
client.subscribe('presence');
});
client.on('message', function (topic, message) {
console.log('User1:',message.toString());
});
stdin.addListener("data", function(d) {
client.publish('msg',d.toString());
});
please give some example code for using nodejs in c# MVC.
I Got a solution for this but i want to clarify that this code uses NodeJs or it is a Native c# Code?
<script>
var client = new Messaging.Client("broker.mqttdashboard.com", 8000, "myclientid_" + parseInt(Math.random() * 100, 10));
client.onConnectionLost = function (responseObject) {
alert("connection lost: " + responseObject.errorMessage);
};
client.onMessageArrived = function (message) {
$('ol').append('<li> ' + $("#TUserName").val() + ' : ' + message.payloadString + '</li><br/>');
};
var options = {
timeout: 3,
onSuccess: function () {
client.subscribe("MQTT/Message", { qos: 2 });
alert("Connected");
},
onFailure: function (message) {
alert("Connection failed: " + message.errorMessage);
}
};
var publish = function (payload, topic, qos, retained) {
var message = new Messaging.Message(payload);
message.destinationName = topic;
message.qos = qos;
message.retained = retained;
client.send(message);
}
$(document).ready(function () {
client.connect(options);
$("#btn").click(function () {
publish($('#test').val(),"MQTT/Message", 2, true);
$("ol").append("<li> You : " + $("#test").val() + "</li>");
});
</Script>
It works good.
but i want to know is this code uses NodeJs or Not?
Please let me Know.
You never directly use node in your client side application. But yes, the functionalities can be accessed by making AJAX or XHR requests to the connection endpoints specified in your node application.
Example
To access MongoDb database in your javascript application, you specify some urls or endpoints in your node application which further invokes the callback function. These callback functions contain your code to connect to a mongo database.
From the client side, you make an AJAX request to the specified url along with the required data. The callback then does the CRUD operations and returns respective result back.
P.S. What you wrote in your answer in C# file inside the <script> tag is pure javascript. Nothing like Node.Js in it.

SignalR WebAPI - Push message not received

My angularclient is able to invoke the serverhub. But never receive push messages from the server, send with this line:
hub.Clients.All.serverTime(DateTime.UtcNow.ToString());
For debug purposes I've added console.log(...) in the subscription (signalrhubproxy.js):
proxy.on(eventName, function (result) {
console.log('event:' + eventName + ' received.' ); <--- never fired
$rootScope.$apply(function () {
if (callback) {
callback(result);
}
});
});
I've followed this tutorial Pushing Data: Integrating with ASP.NET SignalR Hubs wih a few
modifications in my app:
it has a WebAPI structure with a startup.cs, with this SignalR configuration:
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
instead of this in the global.asax:
RouteTable.Routes.MapHubs(new HubConfiguration
{ EnableCrossDomain = true });
AreaRegistration.RegisterAllAreas();
The WebAPI runs on localhost:2034 (VS)
The Client runs on localhost:9000 (grunt)
I'm able to:
- invoke the servermethod getServerTime from the client.
- run the BackgroundServerTimeTimer
- get the hub in the BackgroundServerTimeTimer with:
hub = GlobalHost.ConnectionManager.GetHubContext<ClientPushHub>();
and notifies the clients with:
hub.Clients.All.serverTime(DateTime.UtcNow.ToString()); <--- no error serverside, no glory clientside
Is it my box?
- My box runs on Windows 7 and has VS 2013
UPDATE
I've implemented the simple 'Getting started chatsample' without changes in the server config. And that's working. So, it's clear that it's a clientside problem in my angularjs.
app.js has a value: .value('signalRServer', 'http://localhost:2032/')
The example from the website creates hubproxies like this, without usage of the .value:
function ServerTimeController($scope, signalRHubProxy) {
var clientPushHubProxy = signalRHubProxy(
signalRHubProxy.defaultServer, 'clientPushHub', <--- signalRHubProxy.defaultServer should be: signalRServer
{ logging: true });
var serverTimeHubProxy = signalRHubProxy(
signalRHubProxy.defaultServer, 'serverTimeHub'); <--- signalRHubProxy.defaultServer should be: signalRServer
clientPushHubProxy.on('serverTime', function (data) {
$scope.currentServerTime = data;
var x = clientPushHubProxy.connection.id;
});
$scope.getServerTime = function () {
serverTimeHubProxy.invoke('getServerTime', function (data) {
$scope.currentServerTimeManually = data;
});
};
};
For completeness, the right way:
angular.module('yourApp').controller('yourCtrl', function ($scope, signalRHubProxy, signalRServer ) {
var clientPushHubProxy = signalRHubProxy(
signalRServer, 'clientPushHub',
{ logging: true });
var serverTimeHubProxy = signalRHubProxy(
signalRServer, 'serverTimeHub',
{ logging: true });
clientPushHubProxy.on('serverTime', function (data) {
$scope.currentServerTime = data;
//jshint unused:false
var x = clientPushHubProxy.connection.id;
console.log('Client pushhubproxy id: ' + x );
});
.....
The real problem: I was digging around with different signalR versions and a old version of the example.
Here is the version for SignalR 2 located: signalr angular example

SignalR ConnectionData empty, and won't connect

I have two SignalR hubs in my MVC app, ChatHub and AnotherHub. ChatHub is working nicely, but I can't connect to the other one. I know that SignalR recognises that AnotherHub exists, because I'm getting the following JavaScript in /signalr/hubs:
signalR.chatHub = {
_: {
hubName: 'ChatHub',
ignoreMembers: ['post'],
connection: function () { return signalR.hub; }
},
post: function (room, message) {
return invoke(this, "Post", $.makeArray(arguments));
}
};
signalR.anotherHub = {
_: {
hubName: 'AnotherHub',
ignoreMembers: ['doSomething'],
connection: function () { return signalR.hub; }
},
doSomething: function (thing) {
return invoke(this, "DoSomething", $.makeArray(arguments));
}
};
On the chat page, Fiddler tells me the following when connecting with /signalr/signalr/connect:
connectionData [{"name":"chathub"}]
tid 10
However, when trying to connect to anotherHub, Fiddler says:
connectionData []
tid 3
My javascript on the chat page:
cn = $.connection.chatHub;
$.connection.hub.start();
and on the another page:
cn = $.connection.anotherHub;
$.connection.hub.start();
Now, a curious thing; when I change anotherHub to chatHub on the another page, the connection works. When I change chatHub to anotherHub on the chat page, that also works. The only combination that doesn't is anotherHub on the another page. (I've confirmed this by server-side breakpoints on the hubs' constructors and checking fiddler for the ConnectionData parameter). Any ideas?
Are you sure you have referenced all of the same JS libraries (especially /signalr/hubs) on the another page?

Categories

Resources