Why SqlDependency.OnChange is not being fired? - c#

After struggling with this issue from past 3 day, finally I'm putting my problem here.
I'm trying to build a real time application using SignalR and SqlDependency. Apparently, SQLDependency is not working. However, my SignalR is working fine as I've tried many functions that do not require Database interactions.
Below is my code. Here is I'm taking reference from.
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
NotificationHub objNotificationHub;
protected void Application_Start()
{
string connectionString = WebConfigurationManager.AppSettings["SQL"];
// SQL Command Text
string commandText = "SELECT status From tableTask";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
SqlDependency.Start(connectionString);
command.ExecuteReader().Dispose();
objNotificationHub = new NotificationHub();
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
objNotificationHub.SendNotifications();
}
}
}
NotificationHub.cs (SignalR Hub Class)
[HubMethodName("sendNotifications")]
public void SendNotifications()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.All.recieveNotification("asbc");
}
My SqlDependency.OnChange event i.e. dependency_OnChange is not firing
on database update.
I've tried all the methods like granting permissions
ALTER DATABASE MyDB SET ENABLE_BROKER
and many others like this.
But no success.
Could there be anything that I'm missing. Also, is there any way to check if my code is communicating with SQL Server?
TIA

You are not executing your command, and that is needed to get notification:
SqlDependency.Start(connectionString);
string commandText = "SELECT status From dbo.tableTask"; // don't forget schema here
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
command.ExecuteReader().Dispose();
objNotificationHub = new NotificationHub();
}
Ensure that you understand how those dependencies work (for example - after you receive one notification - you need to re-register to get subsequent). Or better, use some wrapper library, like this one.
You can test it with this simple example:
static void Main(string[] args) {
var cs = "connection string";
using (SqlConnection connection = new SqlConnection(cs))
{
connection.Open();
SqlCommand command = new SqlCommand("select ErrorCode from dbo.Error", connection);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += OnChange;
SqlDependency.Start(cs);
command.ExecuteReader().Dispose();
}
Console.ReadKey();
}
private static void OnChange(object sender, SqlNotificationEventArgs e) {
Console.WriteLine(e.Info);
}

Related

How to detect Database Changes with SqlDependency and c#

I want to fire a Method each Time a database detected a change. I tried this in a Console Application: (ServiceBroker in Database is active)
using System;
using System.Data;
using System.Data.SqlClient;
namespace SqlDependencyExample
{
class Program
{
static void Main(string[] args)
{
string connectionString = "xxx";
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
using (SqlCommand command = new SqlCommand(
"SELECT [ID], [Name] FROM [dbo].[Users]", connection))
{
SqlDependency dependency = new SqlDependency(command);
SqlDependency.Start(connectionString);
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("ID: {0}, Name: {1}", reader.GetInt32(0), reader.GetString(1));
}
}
}
}
private static void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine("Database change detected.");
//Fire something
}
}
}
But the Problem is, the application starts, and instantly writes "Database change detected." and closed and when I´m adding a row in my Database, it is not handling "OnDependencyChange"...
The Console is writing every row which exists. So the Connection is working, but it detects no new rows in the Database
There is nothing that prevents the program to stop, after it executes once.
You can add the Console.ReadLine() statement before the closing bracket of the Main function.

SqlDependency doesn't fire OnChange event when dataset is changed

I'm new to the concept of query notifications with SQL Server and it's going to take some time for me to wrap my head around it.
My objective is to create a Windows service application that is notified when a change has been made to a SQL Server table. I followed this guide which was helpful in getting me started.
However I'm not able to get the expected result. The OnStart() method in my windows service app looks like so:
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Service Started");
serviceRun = false;
SqlClientPermission perm = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);
try
{
perm.Demand();
eventLog1.WriteEntry("permission granted");
}
catch (System.Exception)
{
eventLog1.WriteEntry("permission denied");
}
try
{
connstr = "Data Source=THSSERVER-LOCAL;Initial Catalog=ET;User ID=mujtaba;Password=ths123";
connection = new SqlConnection(connstr);
SqlCommand command = new SqlCommand("select * from dbo.Customer_FileUploads", connection);
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency = new SqlDependency(command);
// Maintain the reference in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange += Dependency_OnChange;
SqlDependency.Start(connstr);
connection.Open();
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
//eventLog1.WriteEntry("reading data");
}
}
else
{
eventLog1.WriteEntry("No rows found.");
}
reader.Close();
}
}
catch (Exception e)
{
eventLog1.WriteEntry("Error Message: " + e.Message);
}
}
The event SqlDependency is subscribed to, looks like so:
private void Dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// Handle the event.
eventLog1.WriteEntry("data changed");
}
The OnStop() method looks like so:
protected override void OnStop()
{
SqlDependency.Stop(connstr);
connection.Close();
eventLog1.WriteEntry("In onStop.");
}
I have ENABLE_BROKER set to true in my database. The end result is, The service runs and the followings logs are created:
"Service Started"
"permission granted"
"data changed"
However when I insert new data into the table, the OnChange() event doesn't fire and no new log is created. Also when I stop and start the service again, the OnChange() is triggered even though there was no new data inserted.
Can anyone help me understand the process?
The SqlDependency is removed after the event fires so you need to execute the command again with the dependency. Below is a console app example that will subscribe again unless the notification was due to an error.
using System;
using System.Data;
using System.Data.SqlClient;
namespace SqlDependencyExample
{
class Program
{
static string connectionString = #"Data Source=.;Initial Catalog=YourDatabase;Application Name=SqlDependencyExample;Integrated Security=SSPI";
static void Main(string[] args)
{
SqlDependency.Start(connectionString);
getDataWithSqlDependency();
Console.WriteLine("Waiting for data changes");
Console.WriteLine("Press enter to quit");
Console.ReadLine();
SqlDependency.Stop(connectionString);
}
static DataTable getDataWithSqlDependency()
{
using (var connection = new SqlConnection(connectionString))
using (var cmd = new SqlCommand("SELECT Col1, Col2, Col3 FROM dbo.MyTable;", connection))
{
var dt = new DataTable();
// Create dependency for this command and add event handler
var dependency = new SqlDependency(cmd);
dependency.OnChange += new OnChangeEventHandler(onDependencyChange);
// execute command to get data
connection.Open();
dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));
return dt;
}
}
// Handler method
static void onDependencyChange(object sender,
SqlNotificationEventArgs e)
{
Console.WriteLine($"OnChange Event fired. SqlNotificationEventArgs: Info={e.Info}, Source={e.Source}, Type={e.Type}.");
if ((e.Info != SqlNotificationInfo.Invalid)
&& (e.Type != SqlNotificationType.Subscribe))
{
//resubscribe
var dt = getDataWithSqlDependency();
Console.WriteLine($"Data changed. {dt.Rows.Count} rows returned.");
}
else
{
Console.WriteLine("SqlDependency not restarted");
}
}
}
}

SqlDependency doesn't fire/trigger event

I have followed some tutorial to send notification to my app when something changes in a table in my SQL Server database. This is my listener class:
class dbListener
{
public dbListener()
{
Debug.WriteLine(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
SqlDependency.Stop(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
SqlDependency.Start(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
connection = new SqlConnection(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
connection.Open();
SomeMethod();
}
SqlConnection connection;
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
SqlCommand command = new SqlCommand("SELECT CODVEI FROM dbo.ArchivioErogazioni", connection);
// Create a dependency and associate it with the SqlCommand.
command.Notification = null; // ---> DO I NEED IT??
SqlDependency dependency = new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// Execute the command.
command.ExecuteReader();
}
// Handler method
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
// Handle the event (for example, invalidate this cache entry).
MessageBox.Show("ikjkjkj");
Debug.WriteLine("fkldjkfjklgjf");
SomeMethod();
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(MainWindow.GetConnectionString("Model"));
}
}
It doesn't fire the event correctly. At the start of the app, it shows me the message box (it is in the event management to test it) once or twice, I don't know why. Then, when I edit the values in the database from SQL Server Management Studio, I get the message box or 0 or 1 or 2 times, then it never fires again.
In my database, I have execute this script:
USE master ;
GO
ALTER DATABASE IN4MATICSystem_Pie SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE
GO
ALTER AUTHORIZATION ON DATABASE::IN4MATICSystem_Pie to sa;
I have surely done a big mistake..... which one??
UPDATE:
After the hints of T McKeown, this is my code (still not working, it shows me 2 or 3 message boxes at startup and then nothing):
class dbListener
{
public dbListener()
{
Debug.WriteLine(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
SqlDependency.Stop(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
SqlDependency.Start(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
connection = new SqlConnection(MainWindow.dbContext.Database.Connection.ConnectionString + "Password=xxx;");
connection.Open();
SomeMethod();
}
SqlConnection connection;
SqlCommand command;
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
if (command == null)
{
command = new SqlCommand("SELECT * FROM dbo.ArchivioErogazioni", connection);
// Create a dependency and associate it with the SqlCommand.
}
else
{
command.Notification = null; // this cancels any previous notifcation object
}
SqlDependency dependency = new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// Execute the command.
command.ExecuteReader();
}
// Handler method
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
// Handle the event (for example, invalidate this cache entry).
MessageBox.Show("ikjkjkj");
Debug.WriteLine("fkldjkfjklgjf");
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= OnDependencyChange;
//dependency.OnChange -= OnDependencyChange;
SomeMethod();
}
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(MainWindow.GetConnectionString("Model"));
connection.Close();
}
}
Try this:
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
// Handle the event (for example, invalidate this cache entry).
MessageBox.Show("ikjkjkj");
Debug.WriteLine("fkldjkfjklgjf");
SqlDependency dependency =
(SqlDependency)sender;
dependency.OnChange -= OnDependencyChange;
SomeMethod(); //re-register
}
Modify SomeMethod():
SqlConnection connection;
SqlCommand command; <-- make command instance var
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
if ( command == null )
{
command = new SqlCommand("SELECT * FROM dbo.ArchivioErogazioni", connection);
// Create a dependency and associate it with the SqlCommand.
}
else{
command.Notification = null; // this cancels any previous notifcation object
}
SqlDependency dependency = new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// Execute the command.
command.ExecuteReader();
}
Call this function, does it return true?
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm =
new SqlClientPermission(
PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}

SqlDependency constant database hits

I have just copied the example from an official Microsoft source http://msdn.microsoft.com/en-us/library/a52dhwx7(v=vs.80).aspx and I am baffled by it. Having run the application, it makes constant database hits even though nothing is using that table? I thought when that table actually changes the event will fire? I don't want it to make constant DB calls every second, that is awful.
Have I done something wrong? I imagine I have not sure what though. Anybody have a link to a good example preferably not MSDN.
Thanks in advance, Onam.
This is the SQL:
return "SELECT [ID],[FromMachine],[FromStore],[FromUser] FROM dbo.Store_Message";
As requested, all code:
public partial class Form1 : Form
{
string connectionString = "server=localhost;database=usicoal;uid=admin;password=";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
GetNames();
}
private bool DoesUserHavePermission()
{
try
{
SqlClientPermission clientPermission = new SqlClientPermission(PermissionState.Unrestricted);
clientPermission.Demand();
return true;
}
catch
{
return false;
}
}
void dep_OnChange(object sender, SqlNotificationEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(GetNames));
}
else
{
GetNames();
}
SqlDependency dep = sender as SqlDependency;
dep.OnChange -= new OnChangeEventHandler(dep_OnChange);
}
private void GetNames()
{
if (!DoesUserHavePermission())
return;
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT ID FROM dbo.[BTE_SIMPLE_STORE_MESSAGE]";
cmd.Notification = null;
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
}
}
}
}
}
}
SqlDependency usually works as you expected it. It fires events if something was changed in your db. Constant calls occure if something is wrong with your query. It's important to use the full two part table name (e.g. dbo.TableName).
The MSDN documentation is not as bad - have a look at this more up-to-date article or this one version.
Found that the above code was fine when I:
Right click database select properties
Select options
Set compatibility level to SQL Server 2008(100)
OK
Bang the code works correctly.

SQLDependency_OnChange-Event fires only one single Time

I'm working with SQLDependency to notify me if there is a change in the Database.
After Program Start-Up it works just fine. When I make a first change the Event fires. Wohoo... that's great.
But if I made a second change the event doesn't fire again. I've searched all the web I think but haven't found anything about THIS Problem. Only found problems where the OnChange-Event fires in a Loop.
Can anyone help me?
Here a little code piece:
private void GetStates()
{
if (!DoesUserHavePermission())
return;
SqlDependency.Stop(con);
SqlDependency.Start(con);
using (SqlConnection cn = new SqlConnection(con))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]"
cmd.Notification = null;
cmd.Dispose();
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
state.Clear(); //In this Case "state" is a List<string>
while (dr.Read())
{
state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
}
dr.Dispose();
dr.Close();
}
}
}
}
my OnChange-Event looks like this:
private void dep_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dep = sender as SqlDependency;
dep.OnChange -= this.dep_OnChange;
using (SqlConnection cn = new SqlConnection(con))
{
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]";
cmd.Notification = null;
if (e.Type == SqlNotificationType.Change)
{
if (cn.State != ConnectionState.Open)
{
cn.Open();
}
using (SqlDataReader dr = cmd.ExecuteReader())
{
state.Clear(); // Clear and Refill the stringlist "state"
while (dr.Read())
{
state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
}
}
}
cn.Close();
}
}
this.GetStates(); //to go ahead and wait for a new change
}
Where is the problem?
I was running into this issue as well. You need to create a new SqlDependency entity (after unsubscribing the existing one from the OnChange event) and then run a new ExecuteReader command. I got the idea from this post:
http://www.codeproject.com/Articles/12335/Using-SqlDependency-for-data-change-events
This usually makes sense, as once you have been notified of a change you will normally want to re-query the data.
After changes happened to the database at the first time, you have to execute the command again and re-subscribe to the event.
The following code is working for me.
class Program
{
static string connectionString = "Server=.;Database=test_sql_dependency;Integrated Security=True;";
static void Main(string[] args)
{
// 1. create database
// 2. enable service broker by executing this sql command on the database.
// alter database test_sql_dependency set enable_broker
// 3. start sql dependency, for some sql server connection string or with queue if you want.
//var queueName = "myFirstQueue";
//SqlDependency.Start(connectionString, queueName);
SqlDependency.Start(connectionString);
// complete the rest of the steps in seperate method to be able to call it again when you need to
// re-subscribe to the event again, becuase by default it will be executed only one time
RegisterSqlDependency();
Console.WriteLine("Listening to database changes...");
Console.ReadLine();
}
static void RegisterSqlDependency()
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
if (connection.State != System.Data.ConnectionState.Open)
{
connection.Open();
}
// 4. create a sql command
// you can't say select *, and also you have to specefy the db owner (dbo.)
SqlCommand command = new SqlCommand("select Id, Name from dbo.Employee", connection);
// 5. create dependency and associtate it to the sql command
SqlDependency dependency = new SqlDependency(command);
// 6. subscribe to sql dependency event
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
// 7. execute the command
using (SqlDataReader reader = command.ExecuteReader())
{
}
}
}
static void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
var InsertOrUpdateOrDelte = e.Info;
//-----------------------------Finally-------------------------
// after you knew that there is a change happened
// you have to unsubscribe the event and execute the command again and then re-subscribe to the event
// 1. unsubscribe the event
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= OnDependencyChange;
// 2. re-subscribe to the event and execute the command again
RegisterSqlDependency();
}
}
In your private void dep_OnChange(object sender, SqlNotificationEventArgs e) method after you unsubscribe the dep_OnChange event you should call the private void GetStates() again, to initialize the dep.OnChange event again.
Not sure if that is your problem but you dispose the command right after you have created it:
using (SqlCommand cmd = cn.CreateCommand())
{
...
cmd.Dispose();
It looks like a bug.
In GetStates() :
SqlDependency.Stop(con);
SqlDependency.Start(con);
these lines should be executed only when registering the sql dependency for first time.
Restrict them when you call the method from OnChange event.
Look my friend:
dep.OnChange -= this.dep_OnChange;
you un-fired your event; which is not true;
just delete this line;

Categories

Resources