65.9K
CodeProject 正在变化。 阅读更多。
Home

事件和 CacheDependency 与数据库

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (8投票s)

2004年9月23日

5分钟阅读

viewsIcon

53139

downloadIcon

642

简单地将事件和 CacheDependency 与数据库实现。

引言

大多数 .NET 应用程序都会使用数据库。这就是我创建这段代码的原因,因为 .NET 1.1 不支持数据库事件。使用这段代码,您可以简单地为数据库中的表实现 OnChangeOnInsertOnUpdateOnDelete 等事件。它的应用范围很广,在任何需要知道数据库中的数据是否已更改或是否有查询在数据库上执行的项目中都很有用。如果您经常使用来自数据库的某些数据,那么每次需要这些数据时都向数据库发出查询是很糟糕的做法。这就是为什么许多程序员在他们的 Web 项目中使用应用程序缓存。他们将一些数据(最常是 DataSet)从数据库放入缓存。那么,当数据库中的数据发生更改并且您需要更新缓存中的数据时,他们会做什么?为了知道数据是否已更改而进行大量查询是很糟糕的做法!一个好主意是使用 CacheDependency 类,但不幸的是,它只支持文件,不支持数据库中的表。这也是我编写代码的原因。这里有一个方法可以返回 CacheDependency 对象,用于在缓存对象和数据库中的表之间建立依赖关系。现在,当数据库中的表发生更改时,您应用程序缓存中的数据库对象就会更新。这段代码是专门为 Microsoft SQL Server 编写的,但您可以将其应用于任何支持触发器和用户定义过程的数据库系统。

使用前

  • 创建目录“C:\Signalfiles\”,并授予“ASPNET”用户读取访问权限。如果您想使用其他目录,则必须在 SqlEvents.cs 中重写静态成员 DefaultPathToSignalFiles 为您的目录路径。并在 xsp_proc.cpp 中重写宏 PATH_TO_SIGNAL_FILES。编译这个 dll 库!请记住,ASPNET 用户必须能够从此目录读取。
  • 将 xsp_proc.dll 复制到“C:\Program Files\Microsoft SQL Server\MSSQL\Binn”或其他 Windows 自动查找 Dll 库的目录。
  • 在您的数据库上执行 InstallMyXSP.sql。例如,您可以这样做:osql -U sa -P -i InstallMyXSP.sql(这仅在您有一个密码为空的用户“sa”时才有效)。此文件会将扩展存储过程(用户定义过程)安装到您的 SQL Server 并授予其必要的权限。
  • 如果您想尝试演示 CacheDependency 与应用程序缓存一起使用的 WebDemoProject,您必须在 Web 服务器上为应用程序目录“WebDemoProject”创建一个名为“WebDemoProject”的虚拟目录。
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

工作原理

当 SQL 查询(仅允许 INSERT、UPDATE 和 DELETE 查询,因为触发器不支持其他查询)在一个由触发器监视的表上执行时。此触发器调用 xsp_proc.dll 中的扩展存储过程 xsp_UpdateFile,该过程会更改(或创建)一个空的信号文件。此信号文件由 FileSystemWatcherCacheDependency 监视,具体取决于您想如何处理这些信息。

SqlEventsObject 类中使用了两个有趣的 SQL 命令。第一个命令用于确保系统中是否存在名为“SomeTrigger”的触发器。有趣的是,您可以使用此查询查找数据库中的其他对象,只需更改类型即可。您可以在数据库服务器的帮助中找到这些类型。

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

下一个命令创建一个名为“SomeTrigger”的触发器,该触发器将监视“TableName”是否由 INSERT、UPDATE、DELETE 查询引起的变化。如果在“TableName”表上执行了其中任何一个查询,将调用扩展存储过程 xsp_UpdateFile,该过程会创建一个名为“file.signalfile”的文件。

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

如何使用

数据库事件

您需要做的第一件事是添加对动态库“SqlEvents.dll”的引用,然后包含命名空间 MiloslavBeno.Data.SqlEvents。然后创建一个 SqlEventsObject 类的实例。构造函数使用 SqlConnection 对象初始化此类的实例。还有一个带 2 个参数的重载构造函数,第一个参数是 SqlConnection 对象,第二个参数是信号文件目录的路径。在 Web 场中使用此代码时(见下文)此参数是必需的。当您拥有 SqlEventsObject 的实例时,调用 CreateSqlEvent,传入数据库名称、表名称以及您想捕获的事件字符串。您可以将此参数设置为“INSERT, UPDATE, DELETE”及其任何组合,而不受顺序影响。因此,此方法会返回事件的标识(即信号文件的名称)。现在,是时候使用 AddHandler 方法了。第一个参数使用 CreateSqlEvent 返回的标识,第二个参数创建 EventHandler 委托,该委托标识将处理事件的方法。当您想从 SqlEventsObject 中删除委托时,请使用 RemoveHandler。就是这样:)
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!");
 }
}

此演示代码是一个控制台应用程序,但您也可以像这样轻松地创建使用 SqlEventsObject 的 Web 应用程序。您只需在 global.asax 文件的 Application_Start 中初始化 SqlEventsObject,而不是在 Main 中。并确保 SqlEventsObject 不会被移除,您可以通过使用 global.asax 文件中 Global 类的变量来引用 SqlEventsObject 实例。

在 Web 场中使用

当 Web 服务器和数据库位于同一台计算机上时,一切都很好。但如果数据库在另一台计算机上呢?Web 场呢?答案是肯定的,这是可能的,但 Web 服务器所在的计算机必须能够访问数据库所在远程计算机的 Win32 文件系统。这是必需的,因为数据库系统的这些事件是基于 Win 32 文件更改通知的。

那么,接下来呢?在 SqlEventsObject 类的构造函数中,将第二个参数设置为信号文件所在远程计算机目录的路径。例如:SqlEventsObject(conn,@"\\RemoteMachineName\\Signalfiles\\")。其他方面与上面控制台应用程序中的相同。

与数据库的 CacheDependency

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

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

如您所见,在这种情况下,您需要做的就是调用 CreateCacheDependency 而不是新的 CacheDependency()。不要忘记,在第一次调用此方法时,需要在应用程序中将第三个参数设置为 true。这意味着方法必须确保数据库中存在所需的触发器。如果不存在,它会创建一个新的。在后续调用中,请使用不带第三个参数的重载函数,或将其设置为 false,因为触发器已在系统中。如果您想看到完整的示例,请参阅 WebDemoProject。

© . All rights reserved.