Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Events and CacheDependency with the database

0.00/5 (No votes)
23 Sep 2004 1  
Simply implement events and CacheDependency with the database

Introduction

Most applications in .NET use database. This is why I made this code, because .NET 1.1 doesn't support events with database. With this code, you can simple implement events like OnChange, OnInsert, OnUpdate or OnDelete with a table in database. The spectrum of using is big, it is useful in every project, where you need to get know that data in the database was changed or if some query was executed on the database. If you use some data from the database more often, it is bad to make a query to the database in every case you need this data. It is why many programmers use application cache in their web projects. They put some data (most often is DataSet) from database to the cache. And now, what will they do if the data in the database was changed and they need to update the data in cache? It is bad to make a lot of queries to know if the data was changed! Good idea is to use class CacheDependency, but unfortunately it supports only files and doesn't support tables in database. It is also why there is my code. There is a method that returns CacheDependency object, prepared to make dependency with the object in the cache and the table in database. Now, your object from the database in the application cache is updated when data in the table in database was changed. This code is specially written for use with Microsoft SQL Server, but you can apply this method to any database system that supports triggers and user defined procedures.

Before using

  • Create directory "C:\Signalfiles\" and grant to it a read access for �ASPNET� user. If you want to use another directory, you have to rewrite in SqlEvents.cs static member DefaultPathToSignalFiles to path to your directory. And in xsp_proc.cpp macro PATH_TO_SIGNAL_FILES. Compile this dll library! Remember, ASPNET user has to be able to read from this directory.
  • Copy xsp_proc.dll to "C:\Program Files\Microsoft SQL Server\MSSQL\Binn" or to some other directory, where Windows automatically look for Dll libraries.
  • Execute on your database InstallMyXSP.sql. For example, you can do it like this: osql -U sa -P -i InstallMyXSP.sql (This will be work only if you have user "sa" with blank password). This file will install extended stored procedure (user-defined procedure) to your SQL server and grant it necessary permissions.
  • If you want to try WebDemoProject that illustrates using of CacheDependency with the application cache, you must make virtual directory named "WebDemoProject" for the application directory "WebDemoProject" on your web server.
InstallMySQL.sql
/*It is necessary to install extended stored procedure to master database*/
USE master 


/* Add external procedure xsp_UpdateFile to Microsoft SQL Server*/
EXEC sp_addextendedproc 'xsp_UpdateFile', 'xsp_proc.dll'


/* Everybody can execute xsp_UpdateFile now*/
GRANT EXECUTE ON xsp_UpdateFile TO PUBLIC



/*Execute*/
GO

How it works

When the sql query (only queries INSERT, UPDATE and DELETE are allowed, because others are not supported by triggers) is executed on a table, which is monitoring with trigger. This trigger calls for extended stored procedure xsp_UpdateFile in xsp_proc.dll that changes (or creates) empty signal file. This signal file is monitored by FileSystemWatcher or CacheDependency, it depends on what you want to do with the information.

There are two interesting SQL commands used in SqlEventsObject class. First one makes sure if a trigger named "SomeTrigger" exists in system. For the interest, with this query you can look for another objects in the database, you only have to do is to change the type. You can find this types in the help of your database server.

SELECT COUNT(name) FROM sysobjects WHERE name = SomeTrigger AND type = 'TR'

Next one creates a trigger called "SomeTrigger", whitch will be watching "TableName" for changes caused by queries INSERT, UPDATE, DELETE. If some of these queries is executed on a table "TableName", extended stored procedure xsp_UpdateFile is called and this procedure creates a file named "file.signalfile".

CREATE TRIGGER SomeTrigger ON TableName FOR INSERT, UPDATE, 
  DELETE AS EXEC master..xsp_UpdateFile 'file.signalfile'

How to use it

Events with Database

First thing you need to do is to add reference to a dynamic library "SqlEvents.dll", after that include namespace MiloslavBeno.Data.SqlEvents. Than create a new instance of SqlEventsObject class. Constructor initializes a new instance of this class with a SqlConnection object. There is also one overloaded constructor with 2 parameters, the first is a SqlConnection object and the second is path to the directory with signal files. This parameter is necessary for using the code in web farms (see below). When you have a instance of SqlEventsObject, call CreateSqlEvent with a name of the database, a name of the table and a string with events you want to catch. You can set this parameter not only to "INSERT, UPDATE, DELETE", but also for its others combinations and it doesn't depend on any order. So, this method returns you identification of the event( it is name of signal file ). Now, it is time for the AddHandler method. As first parameter use identification returned by CreateSqlEvent and as second parameter you create an EventHandler delegate, which identifies the method that will handle the event. And when you want remove delegate from SqlEventsObject use RemoveHandler. And that's all:)
using System;
using System.Data.SqlClient;
using MiloslavBeno.Data.SqlEvents;

class Demo

{
 static void Main(string[] args)
 {

  SqlConnection conn = new SqlConnection(
   "server=localhost;database=TestingDatabase;uid=sa;pwd=");


  SqlEventsObject eo;
  
  try
  {
   conn.Open();


   eo = new SqlEventsObject(conn);



   //This method returns identificator of event with 

   //database table you want to watch for changes.

   string identification = eo.CreateSqlEvent(
    "TestingDatabase","DemoTable","INSERT, UPDATE, DELETE");


   //With identificator returned by method above you can add

   //EventHandler to your method. And you can add 

   //as much EventHandlers as you want

   eo.AddHandler(identification,new EventHandler(OnSqlEvent));
   eo.AddHandler(identification,new EventHandler(OnSqlEvent2));
   //There is also method EventsObject.RemoveHandler 

   //that removes a delegate from the event


 
  // This 2 rows

   Console.WriteLine("Now if you execute query INSERT, UPDATE or "+
    "DELETE on TestingDatabase..DemoTable, event will be called.");
   Console.WriteLine("---===Press a key to quit===---");
   Console.ReadLine();
  }
  catch(Exception e)
  {
   Console.WriteLine(e);
  }
  finally
  {
   conn.Close();
  }
 }



 protected static void OnSqlEvent(Object sender,EventArgs e)
 {
  Console.WriteLine(
   "Table DemoTable in database TestingDatabase was changed!!!");
 }


 protected static void OnSqlEvent2(Object sender,EventArgs e)
 {
  Console.WriteLine("--- Second delagate also works!");
 }
}

This demonstration code is a console application, but you can make web applications with SqlEventsObject as easy like that. You only have to initialize SqlEventsObject in Application_Start at global.asax file instead of in Main. And ensure that SqlEventsObject will not be removed, you can do this by using a SqlEventsObject instance which will be variable of Global class in global.asax file.

Using in Web farms

Everything is ok when a web server and the database is on same computer. But does it work if database is on another computer? and what about web farms? The answer is yes, it is possible, but the computer with a web server must have access to remote machine's Win32 file system, where the database is. It is necessary, because these events database system is based on Win 32 file change notifications.

So, what next? In constructor of a SqlEventsObject class, set second parameter to path to remote machine directory, where the signal files are located. For example: SqlEventsObject(conn,@"\\RemoteMachineName\\Signalfiles\\"). And other things are same like in console application above.

CacheDependency with Database

Context.Cache.Insert("DataSet",
     ds,
     EventsObject.CreateCacheDatabaseDependency(
      "TestingDatabase","DemoTable",true),

     Cache.NoAbsoluteExpiration,
     Cache.NoSlidingExpiration,
     CacheItemPriority.Default,
     new CacheItemRemovedCallback(RefreshDataSet));

As you can see, the thing you have to only do in this case is call CreateCacheDependency instead of new CacheDependency(). Don�t forget that in first call of this method in application is needful to set a third parameter on true. It means that method has to make sure if the necessary trigger is in the database. If it isn�t here, it creates a new one. In next cases use overload function without third parameter or set it to false, because the trigger is already in the system. If you want to see a whole example of this, see WebDemoProject.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here