Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Some applications need to share data between instances, as in a chat program, or receive constant updates, as in a stock broker application. There are many ways to accomplish this, some good, some better. This sample will explore using ADO.NET 2.0 SqlDependency and SQL Server 2005 to provide data flow between separate instances of an application. This simple chat program will demonstrate the usage, and give a test for the potential of these new features.

Cautions

This sample uses features of SQL Server 2005 and ADO.NET 2.0, and was created using Visual Studio 2005.

SQL Server 2005

Among the many new features for SQL Server 2005 are Service Broker and Query Notifications. The Service Broker is a queued, reliable messaging mechanism that is built into SQL Server 2005 and provides a robust asynchronous programming model. The details of SSB are not covered in this article, more information can be found in MSDN.

Query Notifications allow applications to receive a notice when the results of a query have been changed. This improves performance by not having to periodically query the database for changes. More information can be found in MSDN.

Database

The database for this sample is not very elaborate, just enough to demonstrate the features for this article. There are two tables, Message and Person. Person stores the users for the application, and Message stores the messages that are sent. To use SqlDependecy, the database must support a Service Broker. If the database was not created with this option enabled, you will need to enable it.

ALTER DATABASE Chatter SET ENABLE_BROKER

SqlDependency

SqlNotificationRequest can also be used to provide the same services, however, it requires a good deal of manual setup. SqlDependency sets up the plumbing for you. While it is simpler to implement, it obviously doesn't allow the degree of customization that may be necessary for some applications, and SqlNotificationRequest would be the best choice.

A dependency is created between the application and the database via a SqlCommand. Before that can be established, the SqlDependency must be started for this session.

SqlDependency.Start(m_ConnectionString);

After processing this command, SQL Server will automatically create a queue and a service in the database designated in the connection string that is passed in. In order to create this queue, the database user must have the SUBSCRIBE QUERY NOTIFICATIONS permission. As you can see, GUIDs are used for naming these objects. Each time the application is run, a new GUID will be generated, and a new queue and a service created. Although the documentation says these will be removed when the application exits, I have found this to not be the case.

As stated before, the dependency is created based on a SqlCommand.

SqlDependency dependency = new SqlDependency(cmd);

There are some restrictions, of course, as to what can be included in this command. The command must use two part names and not use *. It also must obviously not be an UPDATE or INSERT statement.

The following won't work:

SELECT * FROM Message

This will work:

SELECT ID, Message FROM dbo.Message

If the query is not correct, an event will immediately be sent with:

SqlNotificationEventArgs.Info = Query
SqlNotificationEventArgs.Source = Statement
SqlNotificationEventArgs.Type = Subscribe

The very vague explanation for this is "The statement is not valid for notifications". Unfortunately, I have not been able to find a good source for what comprises a valid statement. In the testing process, I have used an in-line SQL statement which was shown as valid, and then a stored procedure with the very same statement and it was shown as invalid.

Update: Thanks to DylanTheDeveloper for finding this resource describing valid queries: Special Considerations When Using Query Notifications. Also, using SET NOCOUNT ON in your stored procedure will invalidate it for usage in Query Notifications.

Notification

Once the dependency is setup and the application is running, all you have to do is sit back and wait to receive the event.

In this sample application, when a new message is sent by a user, it will be inserted into the database which will cause a message to be sent to the Service Broker Queue and picked up by the Notification Service which will in turn fire the OnChange event to be handled by the application.

void OnChange(object sender, SqlNotificationEventArgs e)
{
  SqlDependency dependency = sender as SqlDependency;

  // Notices are only a one shot deal

  // so remove the existing one so a new 

  // one can be added


  dependency.OnChange -= OnChange;

  // Fire the event

  if (OnNewMessage != null)
  {
     OnNewMessage();
  }
}

The notification is a one shot deal, so after the event has been received, it must be connected again to continue receiving notifications. In the sample application, the OnChange event is removed and an event is fired to the client, which will reload the messages and cause the dependency to be re-established.

public DataTable GetMessages()
{
  DataTable dt = new DataTable();

  try
  {
    // Create command

    // Command must use two part names for tables

    // SELECT <field> FROM dbo.Table rather than 

    // SELECT <field> FROM Table

    // Query also can not use *, fields 

    // must be designated

    SqlCommand cmd = 
        new SqlCommand("usp_GetMessages", m_sqlConn);
    cmd.CommandType = CommandType.StoredProcedure;
    // Clear any existing notifications

    cmd.Notification = null;

    // Create the dependency for this command

    SqlDependency dependency = new SqlDependency(cmd);

    // Add the event handler

    dependency.OnChange += 
           new OnChangeEventHandler(OnChange);

   // Open the connection if necessary

   if(m_sqlConn.State == ConnectionState.Closed)
        m_sqlConn.Open();

   // Get the messages

   dt.Load(cmd.ExecuteReader(
             CommandBehavior.CloseConnection));
  }
  catch (Exception ex)
  {
    throw ex;
  }

  return dt;
}

You'll notice that the connection to the database does not need to be kept open to receive the change event.

Conclusion

This article and sample are just a quick introduction to one of the new features of SQL Server 2005 and ADO.NET 2.0 and how it may be used in an application.

These features are, of course, very new, and information is still somewhat sketchy.

As well as the previously mentioned resources, this blog from Sushil Chordia has some good, though outdated, information about the necessary permissions: DataWorks WebLog.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralSqlDependency Article
Maurice75
10:18 26 Feb '10  
Is the code available for the 'OnNewMessage' function?

void OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;

// Notices are only a one shot deal
// so remove the existing one so a new
// one can be added

dependency.OnChange -= OnChange;

// Fire the event
if (OnNewMessage != null)
{
OnNewMessage();
}
}
GeneralRe: SqlDependency Article
Mark Nischalke
13:11 26 Feb '10  
Have you looked in the download?

I know the language. I've read a book. - _Madmatt

GeneralRe: SqlDependency Article
Maurice75
12:00 28 Feb '10  
I got it from the download.

Thanks a lot!
GeneralI said just one question.... i found another :D
Pheonyx
7:49 4 Feb '10  
From what I understand, the application currently monitors the whole database for changes.

Is it possible to use the same principle but just monitor one specific table??
GeneralRe: I said just one question.... i found another :D
Mark Nischalke
8:12 4 Feb '10  
It does not monitor the entire database. Only the tables specified in the supplied query are used.


I know the language. I've read a book. - _Madmatt

GeneralRe: I said just one question.... i found another :D
Pheonyx
23:12 4 Feb '10  
Okay.. so just to clarify,

The dependancy object is bound to the insertmessage query. When this runs, it triggers a notification resulting in the dependancy knowing the data has changed and calls the getmessages process?
GeneralExcellent Program, just one question
Pheonyx
4:55 4 Feb '10  
Hi,

Firstly I am very impressed with this application, it is an excellent introduction to using SQLDependencies. I have converted the code from c# to vb.net and I have one slight problem.

When my destructor fires it seems to lock up the application.

It just runs the 'SqlDependency.Stop(Me.strConn)' like the c# version, yet it seems to fail.

By fail i mean it tries to run it then just kicks the application out and does not run the next line of code. I have stepped through this and added a catch statement to the Try around it, but no known exception is throw instead the program just exits... eventually....

Any ideas why the one line of code would do this, even though it runs without any problems in the constructor??
GeneralRe: Excellent Program, just one question
Mark Nischalke
5:15 4 Feb '10  
Glad you found the article useful and informative...

Pheonyx wrote:
I have converted the code from c# to vb.net

...but why corrupt perfectly good code by converting it to VB.NET Poke tongue

Have you run the application as is, without converting it, to see the differences?


I know the language. I've read a book. - _Madmatt

GeneralRe: Excellent Program, just one question
Pheonyx
5:19 4 Feb '10  
yes i've run it in c# and i didnt notice the delay im getting.

The reason I converted it is because I want to move the principles to an already developed vb.net application.

The only difference between the two is the performance when closing the application.

The c# exits instantly where as the vb.net one hangs. No technical difference in operation. I've stepped through the code but when i step over the line

"SQLDependency.stop(me.strConn) " is hangs for about 15 - 20 seconds and then kicks out the application without running the finalize.

me.strconn contains the connection string.
GeneralRe: Excellent Program, just one question
Mark Nischalke
6:08 4 Feb '10  
Guess its just proof of C# being superior Poke tongue

They are both .NET languages and should both function the same. Without seeing the application and debugging it I really can't help.


I know the language. I've read a book. - _Madmatt

GeneralHow can i know what operation is performed on table.
jfdrgxdhsgh
0:24 12 Jan '10  
I get the event whenever any insert/update/delete operation is performed on my table.

however how can i know on the application side which operation is performed on the table to take appropriate decision.
GeneralRe: How can i know what operation is performed on table.
Mark Nischalke
2:45 12 Jan '10  
You need to examine the properties of the SqlNotificationEventArgs received by the event handler.

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlnotificationeventargs_properties.aspx[^]


I know the language. I've read a book. - _Madmatt

GeneralLingering queue and service question [modified]
westdw
5:13 11 Jan '10  
In your article you state that each time your application runs, a new queue and service is generated with a GUID on the database side, and that although the documentation states that it is cleaned up after the application exists, you have found it not to be the case.. however (unless I'm overlooking something) you don't state what you do to accommodate this behavior.

What action do you take to make your app clean up after itself before it exits?

Edit:
Oops. I really should have looked at your code first...
        /// <summary>
        /// Destructor
/// </summary> ~ChatData()
{
// Stop the dependency before exiting
SqlDependency.Stop(m_ConnectionString);
}

Excellent article, thanks for your time writing it!

modified on Monday, January 11, 2010 10:21 AM

GeneralRe: Lingering queue and service question
Mark Nischalke
12:11 11 Jan '10  
Thank you


I know the language. I've read a book. - _Madmatt

Generalexcellent article
Member 3824777
6:02 8 Oct '09  
its an excellent article Big Grin . saved my lot of time. Is it possible only to receive the last inserted row?
GeneralRe: excellent article
Mark Nischalke
12:38 8 Oct '09  
Thank you, glad it helped. I'm not usre I understand your question though, the last message received will be from the last operation on the monitored table.


only two letters away from being an asset

QuestionRe: excellent article
Member 3824777
22:26 8 Oct '09  
What i wanted to ask was, the GetMessage() method, it executes the usp_GetMessages procedure which extracts all the messages for the user. Is it possible to extract only the effected record(the last insert or update) for that user instead of extracting all the records?

thanks for your quick response.
AnswerRe: excellent article
Mark Nischalke
2:07 9 Oct '09  
Of course, just add WHERE clause to usp_GetMessage


only two letters away from being an asset

GeneralRe: excellent article
Sunil Munikar
3:23 9 Oct '09  
yeah, that i already tried. but i don't know which row to fetch. I just need to fetch one effected record.
my scenario is, i have 2 records in a table, I update in one of the record using an application and i need to fetch that updated record in another application.
GeneralRe: excellent article
Mark Nischalke
5:20 9 Oct '09  
Of course this article is a basic system for demonstration purposes. The first thought I have is to extended the table and add a timestamp so you you could check for the latest add/update


only two letters away from being an asset

GeneralUsing a non-visual class instead of a form
Kobus du Bruyn
7:35 3 Sep '09  
Thanks a million for your example.

I solved my initial problem but then had to transfer the solution into an application where my 'controlling' class is not a form.

What type of class could I use that is non-visual? My problem arouse when the cast to ISynchronizeInvoke below failed.

void OnNewMessage()
{
ISynchronizeInvoke i = (ISynchronizeInvoke)this;

because 'this' was now my own defined class.

Kobus
GeneralRe: Using a non-visual class instead of a form
Mark Nischalke
8:00 3 Sep '09  
Kobus du Bruyn wrote:
Thanks a million for your example.

Your welcome, I'll take that in a money order please. Wink

The ISynchronizeInvoke is only for the WinForm for thread safety, it isn't required in a non-UI context.


only two letters away from being an asset

GeneralRe: Using a non-visual class instead of a form
Kobus du Bruyn
8:34 3 Sep '09  
Great and thanks for speedy response.
GeneralGetting Infinite
divyesh1432
2:19 27 May '09  
Hi,

I have implemented same thing but it is getting infinite what can be the reason.
The OnChange event fires infinite times.

Thanks
GeneralRe: Getting Infinite
Mark Nischalke
3:18 27 May '09  
Not sure if I understand your problem, could you be a little more vague. Roll eyes


only two letters away from being an asset


Last Updated 28 Jun 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010