使用 SqlDependency 进行数据更改事件
使用 SQL Server 2005 和 SqlDependency 保持应用程序数据最新。
引言
某些应用程序需要实例之间共享数据,例如聊天程序,或者接收持续更新,例如股票经纪应用程序。有许多方法可以实现此目的,有些好,有些更好。本示例将探讨使用 ADO.NET 2.0 的 SqlDependency
和 SQL Server 2005 来在应用程序的不同实例之间提供数据流。这个简单的聊天程序将演示用法,并测试这些新功能的潜力。
注意事项
本示例使用了 SQL Server 2005 和 ADO.NET 2.0 的功能,并且是使用 Visual Studio 2005 创建的。
SQL Server 2005
SQL Server 2005 的众多新功能包括 Service Broker 和查询通知。Service Broker 是内置于 SQL Server 2005 中的一个队列式、可靠的消息传递机制,它提供了一个强大的异步编程模型。本文档不涵盖 SSB 的详细信息,更多信息可以在 MSDN 中找到。
查询通知允许应用程序在查询结果发生更改时收到通知。这通过避免周期性查询数据库以获取更改来提高性能。更多信息可以在 MSDN 中找到。
数据库
本示例的数据库并不复杂,仅足以演示本文的功能。它有两个表:Message 和 Person。Person 存储应用程序的用户,Message 存储发送的消息。要使用 SqlDependecy
,数据库必须支持 Service Broker。如果数据库创建时未启用此选项,则需要启用它。
ALTER DATABASE Chatter SET ENABLE_BROKER
SqlDependency
SqlNotificationRequest
也可以提供相同的服务,但是,它需要大量手动设置。SqlDependency
为您完成了所有连接工作。虽然它实现起来更简单,但显然不允许某些应用程序可能需要的自定义程度,在这种情况下,SqlNotificationRequest
将是最佳选择。
通过 SqlCommand
在应用程序和数据库之间创建依赖关系。在建立该依赖关系之前,必须为此会话启动 SqlDependency
。
SqlDependency.Start(m_ConnectionString);
处理此命令后,SQL Server 将自动在连接字符串中指定的数据库中创建队列和服务。为了创建此队列,数据库用户必须拥有 *SUBSCRIBE QUERY NOTIFICATIONS* 权限。如您所见,GUID 用于命名这些对象。每次应用程序运行时,都会生成一个新的 GUID,并创建新的队列和服务。尽管文档说明当应用程序退出时这些将删除,但我发现情况并非如此。
如前所述,依赖关系是基于 SqlCommand
创建的。
SqlDependency dependency = new SqlDependency(cmd);
当然,此命令包含的内容存在一些限制。该命令必须使用两部分名称,并且不能使用 *
。它显然也不能是 UPDATE
或 INSERT
语句。
以下将不起作用
SELECT * FROM Message
以下将起作用
SELECT ID, Message FROM dbo.Message
如果查询不正确,将立即发送一个带有以下内容的事件:
SqlNotificationEventArgs.Info = Query
SqlNotificationEventArgs.Source = Statement
SqlNotificationEventArgs.Type = Subscribe
对此的解释非常模糊,是“该语句对通知无效”。不幸的是,我一直未能找到一个好的来源来描述什么是有效语句。在测试过程中,我使用了一个内联 SQL 语句,该语句被显示为有效,然后使用相同的语句的存储过程,但被显示为无效。
更新:感谢 DylanTheDeveloper 找到了描述有效查询的资源:使用查询通知时的特殊注意事项。此外,在存储过程中使用 SET NOCOUNT ON
会使其对查询通知无效。
通知
一旦设置了依赖关系并且应用程序正在运行,您所要做的就是坐等接收事件。
在此示例应用程序中,当用户发送新消息时,它将被插入数据库,这会导致消息发送到 Service Broker Queue,并由 Notification Service 拾取,后者将触发 OnChange
事件,由应用程序处理。
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();
}
}
通知是一次性的,因此在收到事件后,必须重新连接才能继续接收通知。在示例应用程序中,会删除 OnChange
事件,并向客户端触发一个事件,该事件将重新加载消息并导致重新建立依赖关系。
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;
}
您会注意到,为了接收更改事件,不需要保持与数据库的连接打开。
结论
本文档和示例只是对 SQL Server 2005 和 ADO.NET 2.0 的一项新功能及其在应用程序中的用法进行简要介绍。
这些功能当然是非常新的,信息仍然有些零散。
除了前面提到的资源之外,Sushil Chordia 的博客还包含一些关于必要权限的良好但已过时信息:DataWorks WebLog。