SqlDependency and Console app monitoring changes - c#

I need your help. I'm trying to implement SqlDependency in a console app to constantly monitor a database table (ultimately a service broker queue). Here is my code and the problem is that after one execution application exits.
class Program
{
static void Main(string[] args)
{
var listener = new Listener();
listener.Listening();
Console.ReadKey();
}
}
public class Listener
{
const string notificationQuery = "SELECT [ID],[Name] FROM [dbo].[tblUsers]";
const string sampleConnectionString = #"Server = xxx; Database = yyy; Integrated Security = SSPI;";
public void Listening()
{
try
{
SqlClientPermission permission = new SqlClientPermission(PermissionState.Unrestricted);
permission.Demand();
}
catch (Exception ex)
{
}
using (var connection = new SqlConnection(sampleConnectionString))
{
connection.Open();
using (var command = new SqlCommand(notificationQuery, connection))
{
SqlDependency.Start(sampleConnectionString);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
var hasChanges = dependency.HasChanges;
command.ExecuteReader();
}
}
}
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine(sender);
Listening();
}
What I found:
how to keep sql dependency doing the its purpose
but adding Listening(); to EventHandler doesn't help. When I'm debugging program and I add some value to the table event is triggered but this adds only one more execution of the Listening method while I want the app to keep working and monitoring the table all the time. Can someone help?

Related

**SignalR** SQLDependency Onchanged fired multiple times

I am having a ASP.Net MVC Monitoring App with VS2013 and .Net Framework 4.5.1 which should automatically refresh the Real-time Chart everytime there are changes in database. Everything works fine when I have a single instance of the browser open, but when i open another tab or browser either on the same machine or on the different machine it fires the SQLDependency Event multiple times.
Here's my Repository.cs
public class Repository
{
private static string conString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
public List<GetTakeInCount> UpdateTRTakeInData()
{
try
{
var lst = new List<GetTakeInCount>();
using (SqlConnection con = new SqlConnection(conString))
{
using (var cmd = new SqlCommand(#"SELECT [Date],[TotalPlan],[TakeInQty],[OrderQty],[DelayedQty] FROM [dbo].[ProductTakeInData] Where [Production] = 'TR'", con))
{
cmd.Notification = null;
if (con.State == ConnectionState.Closed)
con.Open();
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
var rdr = cmd.ExecuteReader();
while (rdr.Read())
{
GetTakeInCount rowData = new GetTakeInCount();
rowData.TakeInQuantity = Convert.ToDouble(rdr["TakeInQty"].ToString());
rowData.Date = rdr["Date"].ToString();
rowData.PlanQuantity = rdr["TotalPlan"].ToString();
rowData.AccPlanQty = Convert.ToDouble(rdr["TotalPlan"].ToString());
rowData.OrderQty = Convert.ToDouble(rdr["OrderQty"].ToString());
rowData.DelayedQty = Convert.ToDouble(rdr["DelayedQty"].ToString());
lst.Add(rowData);
}
rdr.Dispose();
}
}
return lst;
}
catch (Exception e)
{
throw e;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e) //this will be called when any changes occur in db table.
{
if (e.Type == SqlNotificationType.Change)
{
//Call SignalR
MyHub mh = new MyHub();
mh.UpdateChart();
}
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
}
}
Here's my Hub
[HubName("MyHub")]
public class MyHub : Hub
{
public void UpdateChart()
{
List<GetTakeInCount> data = new List<GetTakeInCount>();
Repository rep = new Repository();
data = rep.UpdateTRTakeInData();
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.updateChartData(data);
}
}
Here's my js stuff:
$(function () {
// Declare a proxy to reference the hub.
var chartConnection = $.connection.MyHub;
$.connection.hub.logging = true;
//debugger;
// Create a function that the hub can call to broadcast messages.
chartConnection.client.updateChartData = function () {
updateChart();
console.log('hub started')
};
// Start the connection.
$.connection.hub.start();
updateChart();
});
function updateChart()
{
-- Update Chart Code ---
}
Anyone can help?
Ryan, You can re-look into the event deregister & register part.
"SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);"
I think it's better to have singleton object of SqlDependency and then do the deregistration & registration of that.
To do this You can create a singleton class that will give you an instance of SqlDependency and instead of doing "new SqlDependency(cmd)" you can call the that getInstance(cmd) method of singletonclass and do the deregister & Register on that.

Notify winforms application by Sql Server database changes

I created a Sql server database, in which I added a table named user. Then I executed this script
ALTER DATABASE [TestNotification] SET ENABLE_BROKER
I'd like to use SqlDependency class to notify a winforms application when the user table were changed.
namespace Watcher
{
public partial class Form1 : Form
{
private int changeCount = 0;
private const string statusMessage = "{0} changes have occurred.";
private DataSet dataToWatch = null;
private SqlConnection connection = null;
private SqlCommand command = null;
public Form1()
{
InitializeComponent();
button1.Enabled = CanRequestNotifications();
this.FormClosed += Form1_FormClosed;
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
{
connection.Close();
}
}
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;
}
}
private void button1_Click(object sender, EventArgs e)
{
changeCount = 0;
label1.Text = String.Format(statusMessage, changeCount);
//SqlDependency.Stop(GetConnectionString());
SqlDependency.Start(GetConnectionString());
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
}
if (command == null)
{
command = new SqlCommand(GetSQL(), connection);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private string GetConnectionString()
{
return #"Data Source=BILOG-PRT-12\SQLEXPRESS; Initial Catalog=TestNotification;Integrated Security=True";
}
private string GetSQL()
{
return "Select [id],[nom],[prenom],[age] from [dbo].[user]";
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
MessageBox.Show("modification Occurred");
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
if (i.InvokeRequired)
{
OnChangeEventHandler tempDelegate =new OnChangeEventHandler(dependency_OnChange);
object[] args = { sender, e };
i.BeginInvoke(tempDelegate, args);
return;
}
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
++changeCount;
label1.Text = String.Format(statusMessage, changeCount);
GetData();
}
private void GetData()
{
//dataToWatch.Clear();
//command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
if (connection.State != ConnectionState.Open) connection.Open();
using (var dr = command.ExecuteReader())
{
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
}
}
}
}
All the broker service are running :
I launched the application, Then I clicked into the button and finally I go the Sql Server management studio And I inserted a new row. The problem is that the message box in the application is not shown so the c# application is not notified by SQL Server!!!
So I need to know :
Why this happens?
Which step I forget ?
How can I resolve this issue?
There's quite a few limitations with SqlDependency. To quote one relevant issue:
The projected columns in the SELECT statement must be explicitly stated, and table names must be qualified with two-part names.Notice that this means that all tables referenced in the statement must be in the same database.
(see https://msdn.microsoft.com/library/ms181122.aspx for the full list)
You have to explicitly use two-part name (e.g. dbo.user instead of just user).
Also, you need to execute the command. It doesn't just start working automagically :) Adding a simple using (var dr = command.ExecuteReader()) {} should be enough.

how to start window services automatically while it is stopped by c#

I had created a window services and it will stop when it detect there is changes on my database.
Problem : how to start the window services again after it is stop in maybe 5 or 10 second by coding in C# ?
private static string connectString = ConfigurationManager.ConnectionStrings['ConnStr'].ToString();
int sql_depend = 0;//stop
private delegate void GridDelegate(DataTable table);
private SqlDependency dep;
public Watcher()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
SqlDependency.Start(connectString);
sql_depend = 1;
UpdateGrid();
string file = #"E:\WatcherLogFile\sds.txt";
TextWriter writer = new StreamWriter(file, true);
writer.WriteLine("Window services started");
writer.Close();
}
protected override void OnStop()
{
string file = #"E:\WatcherLogFile\sds.txt";
TextWriter writer = new StreamWriter(file, true);
writer.WriteLine("Window services stopped");
writer.Close();
}
//sql dependency check
private void UpdateGrid()
{
string sql = "select [Name], [ClientName] from [Account]";
DataTable dt = new DataTable();
using (SqlConnection cn = new SqlConnection(connectString))
{
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cn.Open();
dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(dep_OnChange);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
}
}
}
}
//sql dependency detect changes
void dep_OnChange(object sender, SqlNotificationEventArgs e)
{
ServiceController myService = new ServiceController();
myService.ServiceName = "Watcher";
myService.Stop();
SqlDependency.Stop(connectString);
sql_depend = 0;
}
can it be loop function in onStop() ? I want it manually start it once on everyday then it start / stop by itself after that.
You can try this.
var sc = new ServiceController(YouServiceNameString);
// stop service
// sc.Stop();
//start service
sc.Start();
// or restart
/*
if (sc.Status.Equals(ServiceControllerStatus.Running))
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
*/
If you want pause, use Thread.Sleep or this example
var _stopToken = new CancellationTokenSource();
// call this when you want stop
// _stopToken.Cancel();
// there is you can set timeout
_stopToken.Token.WaitHandle.WaitOne(YourWaitTime);
// if you want circle
// while (!_stopToken.IsCancellationRequested)
// { /*...do somthing...*/ }
You have various options to solve your problem.
Easiest solution would be configuring recovery action for a service failure.
Check this link for detailed instructions.
Another option I can think of writing your custom script/program to restart it when you notice the failure.

Firebird sends all events in .NET

Is it normal firebird server sends all registered event counts on raising any of registered event?
By example on firebirds site I do this:
class FirebirdListenerTest
{
public FirebirdListenerTest()
{
try
{
FbConnectionStringBuilder cs = new FbConnectionStringBuilder();
cs.DataSource = "localhost";
cs.Database = "C:\\FIREBIRD\\TEST.GDB";
cs.UserID = "SYSDBA";
cs.Password = "masterkey";
cs.Charset = "NONE";
FbConnection connection = new FbConnection(cs.ToString());
connection.Open();
FbRemoteEvent revent = new FbRemoteEvent(connection);
revent.AddEvents(new string[] { "text_changed", "text_inserted", "justtest_event" });
// Add callback to the Firebird events
revent.RemoteEventCounts += new FbRemoteEventEventHandler(EventCounts);
// Queue events
revent.QueueEvents();
Console.ReadLine();
connection.Close();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
static void EventCounts(object sender, FbRemoteEventEventArgs args)
{
Console.WriteLine("Event {0} has {1} counts.", args.Name, args.Counts);
}
}
In such code, if any of events is raised, I always get counts for all events. Is it how it should works?
Yes it is. You can filter it base on Counts property, it can be 0-n.

showing a progressbar while connection to database

I've created windows application which uses an remote online MYSQL database.For connection i've created a DataConnector() Class. Everytime when i want to connect I create an object of DataConnector() class.
Actually I want to show the progressbar during the connection takes place to database, i mean the progressbar should be on the top of application, after connection successful the progressbar should close automatically.
need some idea how to do it...I've tried with "backgroundworker" but facing prob as the function inside the class returns "MySqlConnection" type.
Here is my DataConnector() Class..
namespace omg
{
class DataConnector
{
bool connected = false;
MySqlConnection connection = null;
MySqlCommand command;
string connectionstring = "server=127.0.0.1;database=online_trading_system;UserId=root;Password=phanny";
public MySqlConnection connect()
{
try
{
connection = new MySqlConnection(connectionstring);
connection.Open();
connected = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return connection;
}
MessageBox.Show("connected with the databse");
// connection.Close();
//connected = false;
return connection;
}
public void close()
{
connection.Close();
connected = false;
MessageBox.Show("connection Closed");
}
}
}
Your BackgroundWorker approach was correct. Here's a short sample:
private void OpenConnectionButton_Click() {
var bw = new BackgroundWorker();
bw.DoWork += (sender, e) => {
// this will happen in a separate thread
var connector = new DataConnector();
connector.connect(); // this might take a while
e.Result = connector;
}
bw.RunWorkerCompleted += (sender, e) => {
// We are back in the UI thread here.
// close the progress bar
...
if (e.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(e.Error.ToString()); // do your error handling here
else {
var connector = (DataConnector)e.Result;
// do something with your connector
}
};
// show the progress bar
...
bw.RunWorkerAsync(); // start the background worker
}

Categories

Resources