OracleCommand cmd =
new OracleCommand("select * from Test WHERE TestFLAG = 1 or TestFLAGis not null", con);
When there is a change at the table, no matter the condition is, my .net project will still receive notification.
For second issue, After I receive any notification for 1st time, any changes on the table after that are not being notified. Why?
Any solution for my problem?
public class MyNotificationSample
{
static string constr = "your db INFO";
public static bool IsNotified = false;
static OracleDependency dep = null;
public static void Main(string[] args)
{
//To Run this sample, make sure that the change notification privilege
//is granted to scott.
OracleConnection con = null;
try
{
con = new OracleConnection(constr);
OracleCommand cmd = new OracleCommand("select * from Test WHERE TestFLAG = 1 or TestFLAGis not null", con);
con.Open();
// Set the port number for the listener to listen for the notification
// request
OracleDependency.Port = 1005;
// Create an OracleDependency instance and bind it to an OracleCommand
// instance.
// When an OracleDependency instance is bound to an OracleCommand
// instance, an OracleNotificationRequest is created and is set in the
// OracleCommand's Notification property. This indicates subsequent
// execution of command will register the notification.
// By default, the notification request is using the Database Change
// Notification.
dep = new OracleDependency(cmd);
// Add the event handler to handle the notification. The
// OnMyNotification method will be invoked when a notification message
// is received from the database
dep.OnChange += OnMyNotificaton;
// The notification registration is created and the query result sets
// associated with the command can be invalidated when there is a
// change. When the first notification registration occurs, the
// notification listener is started and the listener port number
// will be 1005.
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
con.Close();
Console.Write("Press Any Key to Quit");
Console.ReadLine();
// Loop while waiting for notification
}
public static void OnMyNotificaton(object src,
OracleNotificationEventArgs arg)
{
if (dep.HasChanges)
{
Console.WriteLine("Notification Received");
DataTable changeDetails = arg.Details;
Console.WriteLine("Data has changed in {0}",
changeDetails.Rows[0]["ResourceName"]);
}
}
Latest Update: TO make the listener never expired.
new OracleDependency(cmd, false, 0 , true);
But, my query still doesn't work...
add this to your code
cmd.Notification.IsNotifiedOnce = false;
Your query has this WHERE clause: TestFLAG = 1 or TestFLAGis not null.
There's probably a missing space between TestFLAG and is not null. In that case, the first part of the expression is unnecessary, as when TestFLAG = 1, then it's not null.
Maybe the problem is, that your query covers much more ground, than you intended.
Apart from that, the Oracle's Database Change Notifications feature does not guarantee, that you will only get notifications for the rows actually returned by the query. It guarantees, that you will get notifications for those rows, but you can also get "false positives", so notifications for rows which actually do not match your query.
This may be a good explanation from the Oracle Docs (emphasis mine):
Query-based registrations have two modes: guaranteed mode and
best-effort mode. In guaranteed mode, any database change notification
ensures that a change occurred to something contained in the queried
result set. However, if a query is complex, then it cannot be
registered in guaranteed mode. Best-effort mode is used in such cases.
Best-effort mode simplifies the query for query-based registration. No
notifications are lost from the simplification. However, the
simplification may cause false positives, as the simpler version's
query result could change when the original query result would
not.There still remain some restrictions on which queries can have
best-effort mode query-based registrations. In such cases, developers
can use object-based registrations, which can register most query
types. Object-based registrations generate notifications when the
query object changes, even if the actual query result does not. This
also means that object-based registrations are more prone to false
positives than query-based registrations. Developers should be aware
of the relative strengths and weaknesses of each database change
notification option and choose the one that best suits their
requirements.
On the second issue, as #user1415516 wrote, you need to set your notification to not get unregistered after first notification, so add cmd.Notification.IsNotifiedOnce = false; before you execute the command.
Related
Is there a better way than the below to detect if the value retrieved from a database is different to the last retrieved value?
I have a feeling that something better than in infinite poll is available out there?
public void CheckForNewMofificationDate(string username)
{
while(true)
{
OdbcConnection sql = null;
if (!DBClass.Instance.OpenConn(ref sql))
throw new DatabaseConnectionException();
try
{
string query = "SELECT MODIFIED_ON FROM USER_DTLS WHERE USERNAME=?";
using (var cmd = new OdbcCommand(query, sql))
{
cmd.Parameters.Add("USERNAME", OdbcType.VarChar, 50).Value = username;
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
if( OldValue != reader.GetString(0))
{
//use INotifyPropertyChange
}
}
}
}
}
finally
{
DBClass.Instance.CloseConn(ref sql);
}
}
}
Short answer: you would have to employ a polling (looping) mechanism like you suggested.
Or, you could do something crazy with triggers on the database and have the trigger execute a custom function or web service that uses an event bus or WCF to notify your application of a change in data, but I would highly recommend not pursuing this approach.
As recommended by #TimSchmelter, A SqlDependancy is the best approach I found so far, it causes Sql Server to detect changes made to tables assoiciated with a query and fire events based on that:
A SqlDependency object can be associated with a SqlCommand in order to
detect when query results differ from those originally retrieved. You
can also assign a delegate to the OnChange event, which will fire when
the results change for an associated command. You must associate the
SqlDependency with the command before you execute the command. The
HasChanges property of the SqlDependency can also be used to determine
if the query results have changed since the data was first retrieved.
This eliminates the need to have a serprate thread with an infinite loop continuasslt polling to detect changes.
I'm trying to make use of SqlDependancy in a SignalR project, but I can't seem to get the OnChanged event to fire more than once. It fires initially on the subscribe event, but it never fires again after making changes to the underlying database. I've omitted my SignalR and controller code because the problem seems to lie in the repository class. SqlDependancy.Start() is declared in my Global.asax class.
Watching from the SQL server, I can see a notification queue is created when my application starts, and is terminated when I close as well.
public IEnumerable<Visitor> NotifyAllClients()
{
List<Visitor> visitors = new List<Visitor>();
using (var connection = new SqlConnection(new VisitorLogEntities().Database.Connection.ConnectionString))
{
using (var command = new SqlCommand(#"SELECT * FROM dbo.Visitors", connection))
//using (var command = new SqlCommand(#"SELECT [Id],[AgreeToTerms],[Base64Image],[CheckInDate],[CheckOutTime],[Company],[CountryOfOrigin],[email],[FirstName],[LastName],[IsInBuilding],[MeetingSubject],[MeetingTime],[PatriotHost],[phone],[title] FROM dbo.Visitors", connection))
{
var dependency = new SqlDependency(command);
dependency.OnChange += Database_OnChange;
if (connection.State == System.Data.ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
////compile visitor objects
////visitors.add(new Visitor());
}
}
return visitors.OrderByDescending(x => x.CheckInDate);
}
}
private void Database_OnChange(object sender, SqlNotificationEventArgs e)
{
//var dependency = (SqlDependency)sender;
//dependency.OnChange -= Database_OnChange;
////this fires once, with the Type of 'Subscribe', but then never fires on CRUD changes
if (e.Type == SqlNotificationType.Change)
{
VisitorHub.SendVisitors();
}
//NotifyAllClients();
}
edit: lines of code commented out above indicate the changes needed to get this working correctly.
Check this example from msdn http://msdn.microsoft.com/en-US/library/a52dhwx7(v=vs.80).aspx. Download the VS2005_General_en-us.pdf. Page 24636, "Using SqlDependency in a Windows Application" is the section the original link led to. Pay particular attention to step 12 and 13 in the watcher application. In step 12 you will see the removal of the onChange event and then it calls step 13 which sets it up again.
Also, I think you are seeing bad behavior due to your sql statement itself. The sql statement has to follow some rules. See https://technet.microsoft.com/en-us/library/ms181122(v=sql.105).aspx for more info. In particular the Writing Notification Queries section. "The statement may not use the asterisk (*) or table_name.* syntax to specify columns."
I am testing out ServiceStacks OrmLite. I have previosly used MySql without OrmLite and now I am faced with the problem easiest described in this error message:
There is already an open DataReader associated with this Connection
which must be closed first.
Since I have a multi-threaded application, certain threads will be polling the database, while other will insert, update or select "on demand", when needed. This results in the above mentioned exception.
What I need to do is to be able to detect if a connection (IDbHandler) is "busy"; has an open DataReader or something else that. If it is busy, take the next connection (from the "connection pool" i want to implement). The problem is, there is no method or property I can use in the IDbHandler object to determine if it is busy or not.
I have solved this in the "normal" mysql case by simply having a method where I send in the MySqlCommand or just the query string, like:
dbConnections.ExecuteQuery("SELECT * FROM test");
dbConnections.ExecuteQuery(cmd); // cmd == MySqlCommand
and the ExecuteQuery will handle of finding an open connection and just passing on the cmd/query there.
But, since I am using OrmLite it has a lot of extension methods to IDbConnection and I do not want to create "proxy methods" for each one. In the simple mysql case above, there is really only one method needed, that takes in a MySqlCommand, but not so with the many methods in OrmLite.
The first question:
How can I detect if a connection is busy? I want to avoid a try-catch situation to detect it.
Second question:
Is there some way to pass the entire "method" call, something like:
Example:
dbConnections.Run(iDbHandler.Select<MyObject>(q => q.Id > 10));
// or
dbConnections.Run(iDbHandler.Where<MyObject>(q => q.Id > 10));
// or
dbConnections.Run(iDbHandler.SomeOtherWeirdMetod<MyObject>(q => bla bla bla));
This is by far not the best solution, but it is an approach that I am testing with to see how it handles for my specific case (currently on ver 3.97). Like Ted, I am seeing frequent exceptions of open data readers, or connections being returned as closed.
In my usage all services inherit my parent service (which in turn inherits Service) that handles some common meta-data handling. I have opted to have my base service override the Service's Db property and do a quick check on the Db connection's state and attempt a recovery. One case this fixed immediately was the following:
My MsSql server is running in a failover cluster. When the SQL server flips from node A to node B, there is no built-in mechanism that I found in ServiceStack to detect that its in memory connections are "dirty" and need to reconnect.
Comments and improvements are most welcome.
public override System.Data.IDbConnection Db
{
get
{
try
{
var d = base.Db;
if (d == null || d.State != System.Data.ConnectionState.Open)
return ForceNewDbConn();
else
return d;
}
catch (Exception ex)
{
return ForceNewDbConn();
//throw;
}
}
}
private IDbConnection ForceNewDbConn()
{
try
{
var f = TryResolve<IDbConnectionFactory>();
if (f as OrmLiteConnectionFactory != null)
{
var fac = f as OrmLiteConnectionFactory;
fac.AutoDisposeConnection = true;
var newDBconn = fac.Open();
return newDBconn;
}
return base.Db;
}
catch (Exception ex)
{
throw;
}
}
I have an order manager application created in C# and WPF. The order manager application needs to communicate back and forth with a shipping application that is written in a completely different shipping language. The method of communication between the programs is an XML file whose EnableShipping attribute is either a 0 or a 1 and a SQL database.
In my order manager application I have button that "Begins shipping" and changes the EnableShipping attribute from a 0 to a 1. The shipping application is looping and reads this value, begins shipping all of the orders whose certain attribute matches a string, changes this attribute to a different string, and marks a different attribute (Status Changed) to 1.
In my order manager application, as of now I have a thread that continually loops and checks the database for orders with a Status Changed attribute of 1, makes the changes to the UI and writes back to the database, Status Changed = 0.
Here is some code to show what my order manager application is doing in the thread.
while(true)
{
string enabled = null;
currentInstance = OrderManager.getCurrentInstance();
SqlCommand command = new SqlCommand("Select OrderNumber, BatchStatus from dbo.Orders where StatusChanged='1'", connection1);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Int64 orderNum = (Int64)reader[0];
int index = linqForOrderIndex(orderNum);
string batchStatus = (string)reader["BatchStatus"];
SqlCommand statusChangedFalse = new SqlCommand("Update dbo.orders Set StatusChanged = '0' where OrderNumber = '" + orderNum + "'", connection2);
switch (batchStatus)
{
case "Untouched":
currentInstance.Orders[index].batchStatus = "Untouched";
break;
case "Batch Ready":
currentInstance.Orders[index].batchStatus = "Batch Ready";
break;
case "Shipped":
currentInstance.Orders[index].batchStatus = "Shipped";
break;
case "Error":
currentInstance.Orders[index].batchStatus = "Error";
break;
}
statusChangedFalse.ExecuteNonQuery();
Thread.Sleep(1000);
reader.Close();
reader = command.ExecuteReader();
}
currentInstance.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
currentInstance.refreshTreeFilters();
currentInstance.refreshOrderCounts();
currentInstance.batchReadyView();
}
));
}
}
So Even if you don't know exactly what is going on in the code, you know that I have to continuously check the database for status changed items, do work on those items in my collection as well as in the database, update the UI, and keep repeating the process.
At first I thought a good old thread would work like my code is doing now, but the UI becomes unresponsive when I am "doing work". I looked at some background worker code online seeing if maybe that would be a good choice for better UI responsiveness, but didn't know if this is a good solution as I need to continuously keep doing work and updating the UI.
Any thoughts or suggestions? appreciate it...
You could use BackGroundWorker with ReportsProgess to send the info to UI thread. You can pass an object. Also implement cancellation so you you don't shut down stream.
BackgroundWorker.ReportProgress Method
The other option is there is a way to get SQL notification.
Query Notifications in SQL Server
I have a sql query which takes longer than 30seconds to execute. I'm aware I need to set the CommandTimeout for the command object to overcome this. However, the first place the command object occurs is within the method 'LoadDataSet' within the Enterprise Library.
I don't think I want to be modifying it here.
Could someone please suggest to me an appropriate place to set it?
Thanks!
Try this:
dcCommand = dDatabase.GetSqlStringCommand(sSQLCommand);
dcCommand.CommandTimeout = 60; //**This is the key statement**
dDatabase.LoadDataSet(dcCommand, dsDataSet , saTableNames);
Instead of this
dDatabase.LoadDataSet(CommandType.Text, sSQLCommand, dsDataSet , saTableNames);
I have started using Microsoft Enterprise Library long back where in normal case the DB operation calls using provided methods of “Database” class fulfill the need. In some case, for the long running query, developer wants to set CommandTimeout property of SqlCommand (or DbCommand) class. This will allow query to be executed long time as value set in command timeout.
By default Data Access Application block does not support/take simple CommandTimeout parameter in method calls (there are many workaround samples available on net). To achieve the same with minimal changes, I have added a simple function named “WithCommandTimeOut” taking timeOutSecond parameter in “Microsoft.Practices.EnterpriseLibrary.Data.Database” class which returns same instance of “Database” class. Refer updated code snippet below for code changes. Hope this will solve timeout Problem.
//Class Level Static Variables
//Used to reset to default after assigning in "PrepareCommand" static method
static int DEFAULT_COMMAND_TIMEOUT_RESET = 30;
//Default value when "WithCommandTimeOut" not called
static int COMMAND_TIMEOUT_FOR_THIS_CALL = DEFAULT_COMMAND_TIMEOUT_RESET;
public Database WithCommandTimeOut(int timeOutSeconds)
{
COMMAND_TIMEOUT_FOR_THIS_CALL = timeOutSeconds;
return this;
}
protected static void PrepareCommand(DbCommand command, DbConnection connection)
{
if (command == null) throw new ArgumentNullException("command");
if (connection == null) throw new ArgumentNullException("connection");
//Here is the magical code ----------------------------
command.CommandTimeout = COMMAND_TIMEOUT_FOR_THIS_CALL;
//Here is the magical code ----------------------------
command.Connection = connection;
//Resetting value to default as this is static and subsequent
//db calls should work with default timeout i.e. 30
COMMAND_TIMEOUT_FOR_THIS_CALL = DEFAULT_COMMAND_TIMEOUT_RESET;
}
Ex.
Database db = EnterpriseLibraryContainer.Current.GetInstance(Of Database)("SmartSoftware");
db.WithCommandTimeOut(0).ExecuteDataSet(CommandType.Text, query);