How do I monitor changes to my database tables in C#? I'm using SQL Server 2008, and I want to synchronize data when a table has been changed.
If I understand you correctly, you want to create a SqlDependency;
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
connection);
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency=new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange+=new OnChangeEventHandler(OnDependencyChange);
// Execute the command.
command.ExecuteReader();
// Process the DataReader.
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationsEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency
SqlDependency.Stop(connectionString, queueName);
}
Check Microsoft Sync Framework.
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've been trying to figure out how to set up a listener on a database table from my C# code. I've been trying most recently to set up a SqlDependency, but I've gotten stuck because the database I'm trying to apply the procedure on is read-only and I don't have the permissions to change that (yet).
Does anyone know of other ways to go about setting something like this up? My overall goal is to allow my application to listen to a database and be notified when there is an INSERT, UPDATE, etc. and to provide specifically what was changed (i.e. which row).
EDIT: I don't have access to the code which inserts / updates the database, all that I have is read-only access to the database itself.
You'll want to set up a SqlDependency and subscribe to the OnChangeEventHandler. See the example from learn.microsoft.com:
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
using (SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
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+=new
OnChangeEventHandler(OnDependencyChange);
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
// Process the DataReader.
}
}
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(connectionString, queueName);
}
Here's the example from https://msdn.microsoft.com/en-us/library/62xk7953%28v=vs.110%29.aspx
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
using (SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
connection))
{
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency=new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange+=new
OnChangeEventHandler(OnDependencyChange);
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
// Process the DataReader.
}
}
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(connectionString, queueName);
}
How would I use this code to setup SqlDependency in my console.
I tried putting in the start method the Initialization' method, but it does not fireSomeMethod`.
Also -- if it's possible -- I am looking to pass some arguments to my SqlNotificationEventArgs such as the rows.
The Initialization method only opens a connection to the database server. The Start method actually registers a dependency using the command. Your program will need to call both.
OnDependencyChange will be fired when the query result of the aforementioned command changes.
If you look at the reference page for SqlNotificationEventArgs, it does not tell you which rows have changed -- you'll have to query the database yourself to determine that.
I have a C# program that queries the SQL Server database for some values.
Currently the application queries the database every minutes to make sure that the table is up to date.
What I would like to be able to do is that the query is only done when the database has been changed / updated. How do I notify my program when something has been updated in the database?
Thanks
Polling database is not very elegant solution.
SqlDependency from ADO.NET will be useful in your case. It does not use polling but notification mechanism. The notifications are provided by Service Broker in your database, so will need to enable this service in your databse. The OnChange event will raise when specified table changes(update, delete, insert..)
Here is an example how to use SqlDependency:
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
using (SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
connection))
{
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency=new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange+=new
OnChangeEventHandler(OnDependencyChange);
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
// Process the DataReader.
}
}
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(connectionString, queueName);
}
from http://msdn.microsoft.com/en-us/library/62xk7953.aspx
Here is how to enable Service Broker(note that you will have exclusiveness on the database to do that - best do it after restart of the sql server):
http://blogs.sftsrc.com/stuart/archive/2007/06/13/42.aspx(Broken link)
Possible alternative link: http://technet.microsoft.com/en-us/library/ms166086(v=sql.105).aspx
If you are on SQL Server 2005 and above, you can consider using the SqlDependency object.
It represents a query notification dependency between an application and an instance of SQL Server 2005.
An application can create a SqlDependency object and register to receive notifications via the OnChangeEventHandler event handler.
Refer this link on MSDN for more information
However, do note the caveat that MS puts against its use. It is advised to have a caching layer and then use SQLDependency in coordination with that layer .
SqlDependency was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have SqlDependency objects set up for a single database server.
To get a notify when some record is updated, avoid the application to query the table you cab use TableDependency component (in your specific case SqlTableDependency). Here is an example:
public partial class Window1 : Window
{
private IList<Stock> _stocks;
private readonly string _connectionString =
"data source=.;initial catalog=myDB;integrated security=True";
private readonly SqlTableDependency<Stock> _dependency;
public Window1()
{
this.InitializeComponent();
this.McDataGrid.ItemsSource = LoadCollectionData();
this.Closing += Window1_Closing;
var mapper = new ModelToTableMapper<Stock>();
mapper.AddMapping(model => model.Symbol, "Code");
_dependency = new SqlTableDependency<Stock>(_connectionString, "Stocks", mapper);
_dependency.OnChanged += _dependency_OnChanged;
_dependency.OnError += _dependency_OnError;
_dependency.Start();
}
private void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_dependency.Stop();
}
private void _dependency_OnError(object sender, TableDependency.EventArgs.ErrorEventArgs e)
{
throw e.Error;
}
private void _dependency_OnChanged(
object sender,
TableDependency.EventArgs.RecordChangedEventArgs<Stock> e)
{
if (_stocks != null)
{
if (e.ChangeType != ChangeType.None)
{
switch (e.ChangeType)
{
case ChangeType.Delete:
_stocks.Remove(_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
break;
case ChangeType.Insert:
_stocks.Add(e.Entity);
break;
case ChangeType.Update:
var customerIndex = _stocks.IndexOf(
_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
if (customerIndex >= 0) _stocks[customerIndex] = e.Entity;
break;
}
this.McDataGrid.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
this.McDataGrid.Items.Refresh();
}));
}
}
}
private IEnumerable<Stock> LoadCollectionData()
{
_stocks = new List<Stock>();
using (var sqlConnection = new SqlConnection(_connectionString))
{
sqlConnection.Open();
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = "SELECT * FROM [Stocks]";
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
var code = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Code"));
var name = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Name"));
var price = sqlDataReader
.GetDecimal(sqlDataReader.GetOrdinal("Price"));
_stocks.Add(new Stock { Symbol = code, Name = name, Price = price });
}
}
}
}
return _stocks;
}
The event handler is triggered for every INSERT UPDATE or DELETE operation done on the table, reporting you the modified value. So, in case you are interested to keep your C# Datatable up to date, you simple can get the fresh data from the event handler.
What I would like to be able to do is that the query is only done when the database has been changed/updated.How do i notify my program when some thing updated in database.
There isn't any means of the database pushing notifications to the application. The application needs to poll the database to check for updates, and then deal with the updates appropriately.
If by "updates to the database" you mean any update by any application, you're out of luck: it's not doable.
If, however, you mean changes made by your app, it's easy: every time you update the DB raise and event and have handlers respond to the event.