SQLDependency OnChangeEvent not firing - c#

I have the following code to execute a SqlCommand against a database View:
public IEnumerable<PickNote> GetData()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["BTNInternalData_LiveEntities"].ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [PICKINGROUTEID],[CUSTOMER],[SALESNAME]
FROM [dbo].[PickScreenData] WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, [ACTIVATIONDATETIME])) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) or [EXPEDITIONSTATUS] = 3", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
var data = reader.Cast<IDataRecord>();
return data.Select(x => new PickNote
{
pickingRouteId = x["PICKINGROUTEID"].ToString()
}).ToList();
}
}
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// do stuff
}
However, the dependency_OnChange method only gets called at the start of my application and doesn't do so again no matter if the data in my View changes. I've debugged SqlNotificationEventArgs and the Info is Invalid but I'm not sure why as the command query runs fine
Edit
I've changed the query so that it queries the table directly but SqlNotificationEventArgs.Info still says Invalid. Here is the new code:
public IEnumerable<PickNote> GetData()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AXLive"].ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(#"
SELECT PICKINGROUTEID, EXPEDITIONSTATUS
FROM [dbo].[WMSPICKINGROUTE]
WHERE (EXPEDITIONSTATUS <> 20)
AND (DATEADD(dd, 0, DATEDIFF(dd, 0, [ACTIVATIONDATETIME])) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
OR [EXPEDITIONSTATUS] = 3)", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
{
var data = reader.Cast<IDataRecord>();
return data.Select(x => new PickNote
{
pickingRouteId = x["PICKINGROUTEID"].ToString()
}).ToList();
}
}
}
}
WMSPICKINGROUTE is the table where my view was getting the data from before.

According to this MSDN page, you cannot use a SqlDependency against a select statement that references a view. That seems to be the problem. Rewrite your query to hit tables and it should work.

Related

SqlCommandBuilder.SetAllValues is not working anymore

I set the value for my SqlCommandbuilder.SetAllValues = false;
Some year ago when I implemented it I checked with profiler and it worked correct, only the changed fields are in the update command generated by the SqlCommandBuilder.
But now I discovered it is not working anymore.
When debugging I notice that just before calling adapter.Update(table) that in the DataTable the columns have the correct value in the DataRowVersion.Original and the DataRowVersion.Current
I have no clue where to begin looking for this so I am hoping someone can push me in the right direction.
I use binding on all my forms, every control is bound to a BindingSource that is bound to a DataTable.
Here is my complete code of my ApplyUpdate command :
public int ApplyUpdates(DataTable Table, string SelectTextForUpdate, string IdentityFieldName = "") Command, string SelectTextForUpdate = "")
{
int Result = -1;
if (_ConnectionString != null && _ConnectionString != "")
{
using (SqlConnection connection = new SqlConnection(_ConnectionString))
{
connection.Open();
SqlTransaction trans = connection.BeginTransaction();
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
using (SqlCommand command = new SqlCommand())
{
using (SqlCommandBuilder builder = new SqlCommandBuilder())
{
adapter.SelectCommand = command;
adapter.SelectCommand.Connection = connection;
builder.DataAdapter = adapter;
// Make only fields with changed values appear in the update command
// !!!!!!!!!!!!!!!!!!!!!!!!! Somewhere in 2016 this stopped working and it always updates all values whatever the value of this property !!!!!!!!!!!!!!!!!!!!!!!!!
builder.SetAllValues = false;
// Make the where clause of the update statement to have only the primary field in it.
builder.ConflictOption = ConflictOption.OverwriteChanges;
adapter.SelectCommand.CommandText = SelectTextForUpdate;
adapter.SelectCommand.Transaction = trans;
adapter.UpdateCommand = builder.GetUpdateCommand(true).Clone();
adapter.DeleteCommand = builder.GetDeleteCommand(true).Clone();
// create new insertcommand with extra parameter for getting the stupid identity field
SqlCommand inserter = new SqlCommand();
inserter = builder.GetInsertCommand(true).Clone();
if (IdentityFieldName != "")
{
inserter.CommandText += " SET #ID = SCOPE_IDENTITY()";
SqlParameter param = new SqlParameter();
param.Direction = ParameterDirection.Output;
param.Size = 4;
param.DbType = DbType.Int32;
param.ParameterName = "#ID";
inserter.Parameters.Add(param);
}
//put custom insertcommand into our adapter
adapter.InsertCommand = inserter;
// now dispose the original CommandBuilder. The original builder is bound to our adapter and will overwrite the insertcommand
// just before the adapter.update command, and thus dump your new parameter...
// The only way to break this evil spell is to dispose the bad sorcerer
builder.Dispose();
// now create a temperary RowUpdated event, in this we can update the identity field of the table
adapter.RowUpdated += adapter_RowUpdated;
_Table = Table;
_IdentityFieldName = IdentityFieldName;
try
{
try
{
if (adapter.InsertCommand != null)
adapter.InsertCommand.Transaction = trans;
if (adapter.UpdateCommand != null)
adapter.UpdateCommand.Transaction = trans;
if (adapter.DeleteCommand != null)
adapter.DeleteCommand.Transaction = trans;
Result = adapter.Update(Table);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw new Exception(ex.Message);
}
}
finally
{
// get rid of the temperay RowUpdated event
adapter.RowUpdated -= adapter_RowUpdated;
_Table = null;
_IdentityFieldName = "";
inserter.Dispose();
}
Table.AcceptChanges();
}
}
}
}
}

SQLDependency with group by is not working

I have below code for SqlDependency. Below call back function is get called every time when data reader is executed. Rather than this call back function not get executing when i change some value in Table.
public IEnumerable<Messages> GetAllMessages()
{
var messages = new List<Messages>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand("SELECT SUM(MessageID) FROM [dbo].[Messages] group by Message", 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;
}
CallBack:
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
MessagesHub.SendMessages();
}
}
Any Help please!

SQL dependency using stored procedure

I am using SqlDependency in C# code to alert my application when there is a change in a particular column value in a row in database. I could achieve it using inline SQL.
I would like to replace it with stored procedure but for some reason the code isn't executing the stored procedure. I am pasting few lines of code from my application. Please let me know how to modify it so that I can have inline SQL replaced with a procedure call to achieve the same.
var con = new SqlConnection(ConnectionString);
SqlDependency.Stop(con.ConnectionString);
SqlDependency.Start(con.ConnectionString);
var connection = new SqlConnection(con.ConnectionString);
connection.Open();
try
{
using (var command = new SqlCommand("inlinesql", connection))
{
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);
using (SqlDataReader rdr = command.ExecuteReader())
{
try
{
if (rdr.HasRows)
{
_autoEvent.WaitOne();
}
else
{
continue;
}
}
finally
{
rdr.Close();
}
}
}
}
finally
{
connection.Close();
SqlDependency.Stop(con.ConnectionString);
}
void OnDependencyChange(object sender, SqlNotificationEventArgs e)
{
dosomething();
}
Above var dependency... put:
command.CommandType = CommandType.StoredProcedure;
And replace inlinesql with the name of the stored procedure.
Example code that worked for me:
using (var sqlConnection = new SqlConnection (_connectionString)) {
sqlConnection.Open ();
using (var sqlCommand = sqlConnection.CreateCommand ()) {
sqlCommand.Parameters.Clear ();
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
// YOUR STORED PROCEDURE NAME AND PARAMETERS SHOULD COME HERE
sqlCommand.CommandText = "sp_InsertData";
sqlCommand.Parameters.AddWithValue ("#Code", e.Entity.Symbol);
sqlCommand.Parameters.AddWithValue ("#Name", e.Entity.Name);
sqlCommand.Parameters.AddWithValue ("#Price", e.Entity.Price);
try {
var sqlDataInsert = sqlCommand.ExecuteNonQuery ();
} catch (Exception ex) {
MessageBox.Show (ex.Message, this.Title);
}
}
}

Cannot monitor multiple tables with SqlDependency

I have two tables in my db, one that records exceptions, and another that records log messages.
I am leveraging the SqlDependency object to be notified when those tables change so that I can update my web dashboard. I got this working:
public IEnumerable<ElmahException> GetExceptions()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [ErrorId],[Application],[Host],[Type],[Source],[Message],[User],[StatusCode],[TimeUtc],[Sequence],[AllXml]
FROM [dbo].[ELMAH_Error] ORDER BY [TimeUtc] desc", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(ELMAHdependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new ElmahException()
{
ErrorId = x.GetGuid(0),
Application = x.GetString(1),
Host = x.GetString(2),
Type = x.GetString(3),
Source = x.GetString(4),
Error = x.GetString(5),
User = x.GetString(6),
Code = x.GetInt32(7),
TimeStamp = x.GetDateTime(8).ToString().Replace("T", " ")
}).ToList();
}
}
}
private void ELMAHdependency_OnChange(object sender, SqlNotificationEventArgs e)
{
Console.Write("Exception table changed!");
}
This is working well, so with the wind in my sails, I then took a crack at doing something similar for the log messages:
public IEnumerable<LogMessage> GetLogMessages()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [application],[time_stamp],[logLevel],[logger],[message]
FROM [dbo].[LogTable] ORDER BY [time_stamp] desc", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(NLOGdependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new LogMessage()
{
Application = x.GetString(0),
TimeStamp = x.GetDateTime(1).ToString().Replace("T", " "),
LogLevel = x.GetString(2),
Logger = x.GetString(3),
Message = x.GetString(4)
}).ToList();
}
}
}
private void NLOGdependency_OnChange(object sender, SqlNotificationEventArgs e)
{
Console.Write("Log table has changed!");
}
At this point, I am alerted only to when the log table has changed. With this additional SqlDependency in the mix, ELMAHdependency_OnChange never gets called. If I comment out my GetLogMessages() method, then ELMAHdependency_OnChange is called once more.
It looks like multiple SqlDependency objects are mutually exclusive. Any ideas on how I can monitor two tables at the same time?
It is possible to concatenate another SqlStatement using a semicolon.
Here's a snippet from your code, with my changes.
[...]
connection.Open();
var queries = new [] {#"SELECT [application],[time_stamp],[logLevel],[logger],[message] FROM [dbo].[LogTable] ORDER BY [time_stamp] desc",
#"SELECT [ErrorId],[Application],[Host],[Type],[Source],[Message],[User],[StatusCode],[TimeUtc],[Sequence],[AllXml] FROM [dbo].[ELMAH_Error] ORDER BY [TimeUtc] desc"};
using (SqlCommand command = new SqlCommand(string.Join("; ", queries), connection))
{
[...]
It's also important to re-register the SqlDependency once it has called the event. Or else the event is only triggered once..
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
if (e.Type == SqlNotificationType.Change)
{
// Do things
}
SetupDatabaseDependency();
}
SetupDatabaseDependency() would contain the code to set up the SqlDependency.
Use a stored procedure that selects from both tables instead of a query.
CREATE PROCEDURE [dbo].[SQLDependency_TestTable1_TestTable2]
#MaxIdxTestTable1 INT = 1, #MaxIdxTestTable2 INT = 1
AS
-- Don't do this - SQLDependency doesn't like.
--SET TRANSACTION ISOLATION LEVEL READ COMMITTED
-- Don't do this either - SQLDependency doesn't like.
--SELECT MAX(ID) FROM ehmetrology.TestTable1
--SELECT COUNT(ID) FROM ehmetrology.TestTable1
-- See here for a whole list of things SQLDependency doesn't like:
-- stackoverflow.com/questions/7588572/what-are-the-limitations-of-sqldependency/7588660#7588660
SELECT DCIdx FROM TestTable1 WHERE Idx >= #MaxIdxTestTable1
ORDER BY DCIdx DESC;
SELECT DCIdx FROM TestTable2 WHERE Idx >= #MaxIdxTestTable2
ORDER BY DCIdx DESC;
GO
And then do this on the .NET side (pardon the VB):
Using adapter As New SqlDataAdapter(mSQLD_Command)
adapter.Fill(mSQLD_DataSet, SQLD_DATASET_TABLENAME)
End Using
' Reload the dataset that's bound to the grid.
If mSQLD_DataSet.Tables.Count = 2 Then
Dim iTest1Index As Integer = 0
Dim iTest2Index As Integer = 0
If Integer.TryParse(mSQLD_DataSet.Tables(0).Rows(0).Item(0).ToString, iTest1Index) Then
If iTest1Index<> moTest1.MaxDCIdx Then
GetTest1Data(True)
End If
End If
If Integer.TryParse(mSQLD_DataSet.Tables(1).Rows(0).Item(0).ToString, iTest2Index) Then
If iTest2Index <> moTest2.MaxDCIdx Then
GetTest2Data()
End If
End If
End If
By using a stored procedure, you don't have all those records moving around as you do with a consistent select statement. You'll get notified each time either one of the 2 tables are modified, so you have to dig into the result to figure out which one has changed.

load picturebox image from database where image column may be null

there is the code for loading image from database and shows it in picture box. the problem is if there's no pic in a row it will encounter an error however i marked as allow nulls image column in database.
private void button1_Click(object sender, EventArgs e)
{
sql = new SqlConnection(#"Data Source=PC-PC\PC;Initial Catalog=Test;Integrated Security=True");
cmd = new SqlCommand();
cmd.Connection = sql;
cmd.CommandText = ("select Image from Entry where EntryID =#EntryID");
cmd.Parameters.AddWithValue("#EntryID", Convert.ToInt32(textBox1.Text));
var da = new SqlDataAdapter(cmd);
var ds = new DataSet();
da.Fill(ds, "Images");
int count = ds.Tables["Images"].Rows.Count;
if (count > 0)
{
var data = (Byte[])(ds.Tables["Images"].Rows[count - 1]["Image"]);
var stream = new MemoryStream(data);
pictureBox1.Image= Image.FromStream(sream);
}
}
you should check for DbNull.Value.
This should do the trick:
if (count > 0)
{
if (ds.Tables["Images"].Rows[count - 1]["Image"] != DbNull.Value){
var data = (Byte[])(ds.Tables["Images"].Rows[count - 1]["Image"]);
(MemoryStreamstream = new MemoryStream(data)
pictureBox1.Image= Image.FromStream(sream);
}
}
You marked column id DB to accept null value so DB handles it but you also need do handle null in your application (you cannot cast DbNull value).
Hint
While using any kind of Stream you should use Using statement to dispose Stream after usage.
FYI, you need to learn to use using blocks. This will ensure that the objects are Disposed of in a timely manner, even if an exception is thrown:
private void button1_Click(object sender, EventArgs e)
{
var ds = new DataSet();
using (
var sql =
new SqlConnection(
#"Data Source=PC-PC\PC;Initial Catalog=Test;Integrated Security=True")
)
{
using (
var cmd = new SqlCommand
{
Connection = sql,
CommandText =
"select Image from Entry where EntryID = #EntryID"
})
{
cmd.Parameters.AddWithValue(
"#EntryID",
Convert.ToInt32(textBox1.Text));
using (var da = new SqlDataAdapter(cmd))
{
da.Fill(ds, "Images");
}
}
}
var imagesTable = ds.Tables["Images"];
var imagesRows = imagesTable.Rows;
var count = imagesRows.Count;
if (count <= 0)
return;
var imageColumnValue =
imagesRows[count - 1]["Image"];
if (imageColumnValue == DBNull.Value)
return;
var data = (Byte[]) imageColumnValue;
using (var stream = new MemoryStream(data))
{
pictureBox1.Image = Image.FromStream(stream);
}
}
just check
if(ds.Tables["Images"].Rows[count - 1]["Image"]!=DbNull.Value)
{
//you code of execution
}
else
{
//display default image
}

Categories

Resources