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.
Related
I'm trying to connect my DB to my ListView, and I'm trying to find a better way than what's in the book. I looked at a couple forums and a lot of them have the same thing like what's in my code down below.
We didn't have a lot of time to go over databases in class, so a lot of my knowledge with connection strings come from the internet and a small chapter in the book.My Database name is GameStoreLibrary.
using System.Data;
using System.Data.SqlServerCe;
public partial class DisplayGameStoreTable : Form
{
//WHAT THE C# FORUMS SAY TO DO
public SqlCeConnection cn = new SqlCeConnection(#"
Data Source=.;
Initial Catalog=DB GameStoreLibrary;
Integrated Security=True;
MultipleActiveResultSets=True");
private void DisplayGameStoreTable_Load(object sender, EventArgs e)
{
try
{
cn.Open();
}
catch(SqlCeException ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.ExitThread();
}
}
private void NewGameBttn_Click(object sender, EventArgs e)
{
listView1.Items.Clear();
SqlCeCommand cm = new SqlCeCommand("SELECT * FROM newGames ORDER BY gametitle ASC", cn);
try
{
SqlCeDataAdapter da = new SqlCeDataAdapter(cm);
DataTable dt = new DataTable();
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
ListViewItem item = new ListViewItem(dr["gametitle"].ToString());
item.SubItems.Add(dr["releasedate"].ToString());
item.SubItems.Add(dr["console"].ToString());
item.SubItems.Add(dr["company"].ToString());
item.SubItems.Add(dr["gameprice"].ToString());
item.SubItems.Add(dr["quantity"].ToString());
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Small Tip :
Try to use a DBConnect class instead of typing connection string every single time and closing the connection.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace InventoryManagementSystem
{
class DBConnect : IDisposable
{
private static String connectionString = #"Data Source=(LocalDB)\v11.0;AttachDbFilename=D:\Private\InventoryManagementSystem\InventoryManagementSystem\InventoryDB.mdf;Integrated Security=True";
public SqlConnection con = new SqlConnection(connectionString);
public DBConnect()
{
try
{
con.Open();
Console.WriteLine("Database connected");
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.WriteLine("Database Connection Failed");
throw new Exception();
}
}
public void Dispose()
{
con.Close();
}
}
}
After you have this in your project you just have to create an object whenever you want to access the database.
public void getData(){
using(DBConnect db = new DBConnect()){
String q = "select * from TestTable";
SqlCommand cmd = new SqlCommand(q,db.con);
SqlDatareader r = cmd.ExcecuteReader();
}
}
This will automatically close the connections too.
To add on to Gihan's answer, it's also an accepted practice to create the App.Config file and put the connection string in there so it's not inside your source code. Then it's easier to change without recompiling anything.
Use the ConnectionStrings section of the App.Config and then you can get the connection string using the code:
System.Configuration.ConfigurationManager.ConnectionStrings["MyDBConnectionString"].ConnectionString;
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);
}
I am self-learning C# using Visual Studio 2012 and stuck on a connection problem. Basically, I want to use a combobox to connect to a database based on the users selection.
For example: When the user selects TEST1 this will select the test1 database and TEST2 will enable test2 database..etc
The code I have pieced together uses a button which displays the results from a SQL script through a messagebox. At the moment I cant get this to work as the message box does not display anything.
I commented out the MainConnection() as that was a test to see if the connection was working.
Appreciate if someone could point me in the right direction.
Please see the C# code below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace TestDB
{
public partial class Form1 : Form
{
class ComboItemExample
{
public string DisplayString { get; set; }
public string ConnectionString { get; set; }
public override string ToString() { return DisplayString; }
}
private string currentConnection = "Data Source= np-2 ;Initial Catalog= TESTDB Integrated Security=true";
public Form1()
{
InitializeComponent();
var firstConnection = new ComboItemExample { DisplayString = "Data Source= np-2 ;Initial Catalog= TESTDB1 Integrated Security=true" };
comboBox1.Items.Add("TEST1");
var secondConnection = new ComboItemExample { DisplayString = "Data Source= np-2 ;Initial Catalog= TESTDB2 Integrated Security=true" };
comboBox1.Items.Add("TEST2");
}
public void MainConnection()
{
//Make connection to np-2 TESTDB
//string str = "Data Source= np-hums12 ;Initial Catalog= TESTDB;"
//+ "Integrated Security=true";
// ReadOrderData(str);
}
public static void ReadOrderData(string currentConnection)
{
// Run SQL script
string queryString = "SELECT *;";
using (SqlConnection connection = new SqlConnection(currentConnection))
{
SqlCommand command = new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
}
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// call read before accessing data.
while (reader.Read())
{
//display script in message box
MessageBox.Show(reader.GetValue(1).ToString());
}
// close when finished reading.
reader.Close();
}
}
private void CloseUI_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void ShowData_Click(object sender, EventArgs e)
{
MainConnection();
}
private void comboBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex <= 0) return;
var newConnection = ((ComboItemExample)comboBox1.Items[comboBox1.SelectedIndex]).ConnectionString;
// use "newConnection" as connection string.
currentConnection = newConnection;
using (var connection = new SqlConnection(currentConnection))
{
}
}
}
}
It looks like you're just adding the text value to your comboboxes, but not actually tying the connection string to it. You may be inadvertently passing the values "TEST1" and "TEST2" as connection strings. You should be adding the new variables you're creating in your constructor as the new items, not those strings. Use the Add() that takes an Item as a parameter.
mycombobox.Add(new Item("Test1", firstConnection));
Assuming your connection strings are correct and functioning, the reason it is displaying nothing is because it is throwing an error with your SQL.
This line is where your mistake lies
string queryString = "SELECT *;";
It is not a valid SQL query, you need to also specify a table. To help in the future, its is often wise to use try-catch statements to help identify potential errors.
For example in your code you could use
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
try
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
MessageBox.Show(reader.GetValue(1).ToString());
}
//dont need to close the reader as the using statement will dispose of it once finished anyway
}
connection.Close();
}
catch (Exception ex)
{
connection.Close();
Console.WriteLine(ex.Message);
//or, depending on the package Debug.WriteLine(ex.Message);
}
}
This will print out the exception and stop your program locking up too. As it stands, your current one won't throw an error saying nothing was found, but it will throw an SQL exception in the output log but won't give you details. Exeception.Message will give you details, or SqlException.Message can provide SQL related messages.
Edit: In response to the comment you posted (and something I missed previously)
Looking at the way you have added your ComboBox items, you haven't even added the objects that you think you have. From your code, your combobox items will be "TEST1" and "TEST2" - not the connection strings.
Instead, you could add your objects to the box like so
comboBox1.Items.Add(new ComboItemExample() {DisplayString ="TEST1",ConnectionString = "Data Source= np-2 ;Initial Catalog= TESTDB1 Integrated Security=true"});
comboBox1.Items.Add(new ComboItemExample() {DisplayString ="TEST2",ConnectionString = "Data Source= np-2 ;Initial Catalog= TESTDB2 Integrated Security=true"});
comboBox1.DisplayMember = "DisplayString";
comboBox1.ValueMember = "ConnectionString";
Then to retrieve the value from the combobox for your query
string myConnectionVal = comboBox1.SelectedValue.ToString();
The reason you are getting the cast error is because you never assigned the ComboItemExample to the combobox in the first place. With the item adding code above you would be able to do this in the future, but if all you need is a single value from the object, ValueMember is easier to use.
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");
}
}
}
}
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;