SqlDependency constant database hits - c#

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.

Related

Why SqlDependency.OnChange is not being fired?

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);
}

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");
}
}
}
}

How to load records in mysql to one form from another form by separate by commas C#

I want to get mysql from one form by clicking a button and then it goes to another form and show the data on a textbox separating by commas.
Here is my code:
private void button3_Click(object sender, EventArgs e)
{
connection = new MySqlConnection("datasource=localhost;port=3306;Initial Catalog='yadb';username=root;password=");
//command = connection.CreateCommand();
string query = "SELECT `tpno` FROM `member` WHERE `electorate_cluster`='"+comboBox1.Text+"'";
command = new MySqlCommand(query, connection);
try
{
connection.Open();
//MySqlDataAdapter mda = new MySqlDataAdapter("SELECT `tpno` FROM `member` ", connection);
reader = command.ExecuteReader();
while (reader.Read())
{
//string s = "tpno";
sms sm = new sms();
sm.mobno.Text = reader.GetString("tpno".Split(","));
}
}
catch (Exception)
{
throw;
}
finally
{
if (connection != null)
{
connection.Close();
}
}
}
In here I used mysql database to store data and mobno.text means the 2nd form textbox
Btw I am new to C#
you can have parameterized constructor for your second form...and pass data what you need from frist form and make sure your paramterized constructor is calling default constructor.
Something like this
public class sm : Form
{
public sm()
{
IntializeComponent();
}
public sm(string data):this()
{
//Assign the data wherever you want
}
}
In first form button click event do this
while (reader.Read())
{
//string s = "tpno";
sms sm = new sms(reader.GetString("tpno".Split(",")));
sm.Show();
}

ASP.Net C# - Using a DataTable

I hope this makes sense. I'm familiar with using DataReaders, however I think using a DataTable in this instance makes sense. I have a database table that includes 'questions' and I want to implement a facility to be able to browse through the questions on a website.
So, simplified, there's a label to output the question (lblQuestion) and a back button and a forward button to go to the previous/next question.
Is the following code/saving the DataTable in a Session object the most efficient way to do this?
protected void Page_Load(object sender, EventArgs e)
{
if (!(Page.IsPostBack))
{
getQuestions();
int questionCounter = 1;
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
}
protected void getQuestions()
{
string connStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
MySqlConnection conn = new MySqlConnection(connStr);
MySqlDataReader reader;
string cmdText = "SELECT * FROM questions WHERE approved='Y' AND module_id=1";
MySqlCommand cmd = new MySqlCommand(cmdText, conn);
try
{
conn.Open();
reader = cmd.ExecuteReader();
DataTable dt1 = new DataTable();
dt1.Load(reader);
reader.Close();
Session["dt1"] = dt1;
lblQuestion.Text = dt1.Rows[0]["question"].ToString();
}
catch
{
lblError.Text = "Database connection error - failed to get questions.";
}
finally
{
conn.Close();
}
}
protected void btnNext_Click(object sender, EventArgs e)
{
int questionCounter = Convert.ToInt32(ViewState["questionCounter"]);
DataTable dt1 = (DataTable)Session["dt1"];
if (questionCounter < dt1.Rows.Count)
{
questionCounter++;
lblQuestion.Text = dt1.Rows[questionCounter-1]["question"].ToString();
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
else
{
lblQuestion.Text = "the end of questions.";
}
}
protected void btnBack_Click(object sender, EventArgs e)
{
int questionCounter = Convert.ToInt32(ViewState["questionCounter"]);
DataTable dt1 = (DataTable)Session["dt1"];
if (questionCounter > 1)
{
questionCounter--;
lblQuestion.Text = dt1.Rows[questionCounter-1]["question"].ToString();
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
else
{
lblQuestion.Text = "the beginning of questions.";
}
}
In general you shouldn't really store data in the session object. The reason for this is that every user will have that datatable stored against them. That means if the table is 1MB and you have 100 users, then you will have 100MB of session objects. Obviously this is not very good.
Instead what you can do is use the Application Cache. This is different to the session store as it is shared across all users (and is cleared upon IIS Application Pool recycle). It's as easy as saving to the session object, but you just use the Cache instead.
In your code you should check to see if the cache contains the questions table already. If it does then just return it, otherwise do your SQL query to populate it. If your questions tend to change regularly then consider using expiration timers that will automatically remove the object after a period of time you define. Then the next time a user queries the question table, your code will detect that it has been removed and request it again.

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