찾다
데이터 베이스MySQL 튜토리얼使用SqlDependency监听SqlServer2005数据库变更通知

背景需求:对于数据中表A数据字段的变更,需要引发相应业务逻辑,插入或更新相关表或字段。在以往的方式我们多会在数据库端下文章,建立相应触发器,来完成业务逻辑操作。不过这种方式仅适用于单纯对于数据操作的需求,可是当我们要完成更复杂的业务需求是却

背景需求:对于数据中表A数据字段的变更,需要引发相应业务逻辑,插入或更新相关表或字段。在以往的方式我们多会在数据库端下文章,建立相应触发器,来完成业务逻辑操作。不过这种方式仅适用于单纯对于数据操作的需求,可是当我们要完成更复杂的业务需求是却不太容易了(虽然sql05已经支持托管代码的使用了)。可能你会想到我们可以轮询数据库相关表或视图,来发现数据的变化,可是这对于性能和即时性却是个不容易取舍的问题。不过在SqlServer2005中有了新的方案,那就是查询通知。

        查询通知是在 Microsoft SQL Server 2005 中以及 ADO.NET 2.0 的 System.Data.SqlClient 命名空间中引入的。查询通知建立在 Service Broker 基础结构的基础上,使应用程序可以在数据更改时收到通知。如果应用程序提供数据库中信息的缓存(例如 Web 应用程序),需要在源数据更改时接收通知,此功能特别有用。

通过三种方式可以使用 ADO.NET 实现查询通知:

  1. 低级实现由 SqlNotificationRequest 类提供,该类公开服务器端功能,使您可以对通知请求执行命令。

  2. 高级实现由 SqlDependency 类提供,该类提供源应用程序与 SQL Server 之间通知功能的高级抽象,使您可以使用相关性来检测服务器中的更改。大多数情况下,这是托管客户端应用程序通过适用于 SQL Server 的 .NET Framework 数据提供程序利用 SQL Server 通知功能的最简单、最有效的方法。

  3. 此外,使用 ASP.NET 2.0(或更高版本)构建的 Web 应用程序可以使用 SqlCacheDependency 帮助器类。

        如果应用程序需要通过刷新显示或缓存来响应基础数据中的更改,查询通知非常有用。如果执行相同命令生成的结果集与最初检索到的结果集不同,则 Microsoft SQL Server 可允许 .NET Framework 应用程序向 SQL Server 发送命令和请求通知。服务器上生成的通知通过队列发送,供以后处理。

您可以为 SELECT 和 EXECUTE 语句设置通知。使用 EXECUTE 语句时,SQL Server 会为执行的命令而不是 EXECUTE 语句本身注册通知。该命令必须满足 SELECT 语句的要求和限制。当注册通知的命令包含多个语句时,数据库引擎会为批处理中的每个语句创建一个通知。

使用查询通知的应用程序有一组通用的要求。必须正确配置数据源才能支持 SQL 查询通知,并且用户必须具有正确的客户端和服务器端权限。

要使用查询通知,必须符合下列条件:

1.使用 SQL Server 2005 或 SQL Server 2008。

2.对数据库启用查询通知。

3.确保用于连接数据库的用户 ID 具有必要的权限。

4.使用 SqlCommand 对象执行有效的 SELECT 语句,包含关联的通知对象 — SqlDependency 或 SqlNotificationRequest。

5.提供所监视的数据更改时用于处理通知的代码。

下面就以一个例子来说明使用SqlDependency的整个流程

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using System.Windows.Forms;

namespace CaptureWeb
{
    public class SQLServiceBroker
    {

        private string connectionStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();

        private string sqlStr = "";

        private SqlConnection connection = null;

        public delegate void UIDelegate();

        private UIDelegate uidel = null;

        public Form form = null;

        ///
        /// 
        ///
        ///
        ///
        public SQLServiceBroker(string TableName, List ColumnNames)
        {
            string columns = "";
            foreach (string str in ColumnNames)
            {
                if (columns != "")
                    columns = columns + ",";
                columns = columns + "[" + str + "]";
            }
            this.sqlStr = string.Format("select {0} From [dbo].[{1}]", columns, TableName);
        }

        ///
        /// 
        ///
        ///
        ///
        ///
        public SQLServiceBroker(string constr, string TableName, List ColumnNames)
            : this(TableName, ColumnNames)
        {
            this.connectionStr = ConfigurationManager.ConnectionStrings[constr].ToString();
        }


        ///
        /// 
        ///
        ~SQLServiceBroker()
        {
            StopDependency();
            connection.Dispose();
        }

        ///
        /// 
        ///
        ///
        public bool EnoughPermission()
        {

            SqlClientPermission perm = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);
            try
            {
                perm.Demand();
                return true;
            }
            catch (System.Exception)
            {
                return false;
            }
        }

        ///
        /// 
        ///
        ///
        public void InitDependency(UIDelegate uidelegate)
        {

            SqlDependency.Stop(connectionStr);
            SqlDependency.Start(connectionStr);
            if (connection == null)
                connection = new SqlConnection(connectionStr);

            if (!EnoughPermission())
                throw new Exception("没有权限(SqlClientPermission)!");
            if (uidelegate == null)
                throw new Exception("回调方法未指定(UIDelegate)!");
            if (connection == null)
                throw new Exception("未初始化(InitDependency)!");
            this.uidel = uidelegate;

        }

        ///
        /// 传入窗体对象,以防止委托有需要访问UI层控件是引发的“从不是创建控件的线程访问它”
        ///
        ///
        ///
        public void InitDependency(Form form1, UIDelegate uidelegate)
        {
            InitDependency(uidelegate);
            this.form = form1;
        }

        ///
        /// 
        ///
        public void StartDependency()
        {
            //这里很奇怪,每次都需要新的command对象
            using (SqlCommand command = new SqlCommand(sqlStr, connection))
            {
                command.Notification = null;
                SqlDependency dependency = new SqlDependency(command);
                dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                if (connection.State != ConnectionState.Open)
                    connection.Open();
                command.ExecuteNonQuery();
                command.Dispose();
            }
        }

        ///
        /// 
        ///
        public void StopDependency()
        {
            SqlDependency.Stop(connectionStr);
            if (connection != null)
                connection.Close();
        }

        ///
        /// 
        ///
        ///
        ///
        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            //注销监测事件
            SqlDependency dependency = (SqlDependency)sender;
            dependency.OnChange -= dependency_OnChange;
            //放在移除事件之后又很大必要,防止ui层调用更新相同表时,进入循环出发调用
            //uidel.Invoke();
            //uidel();
            //使用from.Invoke调用防止访问界面控件引发“从不是创建控件的线程访问它”
            if (form != null)
                form.Invoke(uidel);
            else
                uidel();
            //再次启动监听
            StartDependency();
        }

    }
}

调用方式:

SQLServiceBroker broker;
private void button1_Click(object sender, EventArgs e)
{
    //需要监测的列
    List columns = new List();
    columns.Add("test1");
    columns.Add("test2");
    string table = "test";
    broker = new SQLServiceBroker(table, columns);
    //实例化毁掉函数
    SQLServiceBroker.UIDelegate uidel = new SQLServiceBroker.UIDelegate(writeCon);
    //初始化,及传入回调函数
    broker.InitDependency(uidel);
    //初始化,传入窗体对象对于需要委托中访问ui控件的情况
    //broker.InitDependency(this, uidel);
    //启动监听
    broker.StartDependency();
    MessageBox.Show("启动");
}

private void writeCon()
{
    MessageBox.Show("changed");
}

代码比较简单,都有说明,这里有必要注意几点问题:

1.首先需要在SQL Server 2005上执行ALTER DATABASE SET ENABLE_BROKER;语句让相应的数据库启用监听服务,以便支持SqlDependency特性。


2.对于SqlCommand的cmdText有特殊要求,其中不能用*,不能用top,不能用函数,包括聚合函数,不能用子查询,包括where后的子查询,不能用外连接,自连接,不能用临时表,不能用变量,不能用视图,不能垮库,表名之前必须加类似dbo数据库所有者这样的前缀。


3.其中在使用当中发现SqlConnection对象应该是一直存在的,因此在此示例中升级为属性,如果将它声明在StartDependency方法体中,出现只能调用一次的情况,因为对于SqlDependency需要connection对象的存在。


4.在使用委托传入调用方法是,如果方法有访问界面UI控件的情况,需要传入窗体对象,以form.Invoke(uidel);的方式调用,否则会引发“从不是创建控件的线程访问它”异常。


5.对于回调函数中需要更新正在监听的表时防止循环调用造成死循环,请在调用委托之前先移除onchange事件dependency.OnChange -= dependency_OnChange;


整个项目Demo:http://cid-9601b7b7f2063d42.skydrive.live.com/self.aspx/Code/SQLServiceBroker.rar


http://support.microsoft.com/kb/555893/


CodeProject:http://www.codeproject.com/KB/database/chatter.aspx


MSDN: http://msdn.microsoft.com/zh-cn/library/t9x04ed2.aspx


出现的问题:sql  Service Broker 未开启


开启方法:http://pan.baidu.com/share/link?shareid=1850603785&uk=3675091944


未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker。 
 
 
 
解决办法: 
在Microsoft SQL Server Management Studio Express新建查询中执行如下语句 
alter database DotShoppingCart set enable_broker 
 
若命令执行成功的话,验证一下,执行下面SQL语句 
select IS_BROKER_ENABLED from master.sys.databases where name='DotShoppingCart'  
若出现下图所示,则配置成功 
 
   
注:DotShoppingCart是一个数据库     












 2 
 
扩展阅读  
Service Broker 端点 
SQL Server 使用 Service Broker“端点”使 Service Broker 与 SQL Server 实例外部进行通信。 端点就是一个 SQL Server 对象,代表 SQL Server 进行网络通信的功能。每个端点支持一种特定的通信类型。例如,HTTP 端点使 SQL Server 可以处理特定的 SOAP 请求。Service Broker 端点将 SQL Server 配置为通过网络发送和接收 Service Broker 消息。 
Service Broker 端点为传输安全模式和消息转发提供选项。Service Broker 端点侦听特定的 TCP 端口号。 
默认情况下,SQL Server 的实例不包含 Service Broker 端点。因此,默认情况下 Service Broker 不通过网络发送或接收消息。必须创建 Service Broker 端点才能向 SQL Server 实例外部发送消息或从该实例外部接收消息。有关创建 Service Broker 端点的详细信息,请参阅 CREATE ENDPOINT (Transact-SQL)。一个实例可以只包含一个 Service Broker 端点。 
安全说明 
创建 Service Broker 端点后,SQL Server 将接受在该端点中指定的端口上的 TCP/IP 连接。Service Broker 传输安全模式要求授权后才能连接该端口。如果运行 SQL Server 的计算机启用了防火墙,则该计算机上的防火墙配置必须允许该端点中所指定端口的传入和传出连接。有关 Service Broker 传输安全模式的详细信息,请参阅 Service Broker 传输安全性。 
  
Service Broker 传输安全性 
Service Broker 传输安全性使数据库管理员可以对与数据库的网络连接进行限制并且可以加密网络上的消息。Service Broker 端点支持基于证书的身份验证和 Windows 身份验证。 
传输安全性应用于两个实例间的网络连接。传输安全性控制哪些实例可以通信并提供两个实例间的加密。 
传输安全性作为一个整体应用于实例。传输安全性不保护各个消息的内容,也不控制对实例中各个服务的访问。Service Broker 对话安全性在各消息离开发送实例至到达目标实例之间,对消息进行加密。 实例使用的身份验证类型取决于每个实例的 Service Broker 端点的 AUTHENTICATION 选项。当端点指定多个身份验证方法时,具体使用哪个身份验证方法取决于为发起连接的实例指定这些方法时的












 3 
顺序。协商期间,每个实例都报告其所支持的所有身份验证类型和算法。发起方按照接受方指定的顺序尝试两个端点都支持的身份验证方法。这意味着,对于长时间运行的会话,消息可能通过多个连接来交换,并且连接的身份验证可能随着发起会话的实例不同而不同。 
Service Broker 端点支持两种类型的加密。与身份验证一样,具体用于连接的加密方法取决于为发起连接的实例指定这些方法时的顺序。 
   
解析SQL Server 2005中的Service Broker 
来源:68design.net 2007年08月29日 09:08 网友评论:0条 点击: 
740 
  SQL Server 2005中的新内容Service Broker,可用来建立以异步消息为基础的应用。Service Broker应用是一个或者多个程序的集合,能够完成一套相关的任务。为了更加深入的了解其涵义,让我们来看看组成应用的各个对象。    消息器  
  消息是Service Broker应用中信息传递的基本单元。在Service Broker内部,消息是按发送顺序进行接收,并且保证每个消息只会发送和接收一次。而且消息保证不会丢失。有时,某个消息已被发送,但是没有马上收到。当发生这种情况时,Service Broker会保存消息并尝试再次发送。消息带有确认信息以确保经他们传递的信息就是他们所等待接收饿。可以传递的消息最大可达2G。    会话  
  当消息在Service Broker应用中传递使使用会话(或者对话)方法。会话一般针对特别任务生成,当任务完成以后就会被删除。会话才是Service Broker最主要的信息交换结构,而不是消息。会话发生在两个服务端点之间:发起会话的服务(发起方),以及接受会话需求的服务(目的方)。    队列  
  在Service Broker应用中,消息以队列方式保存等待接受处理。在内部,Service Broker队列是一种特殊的表格,能够通过指明队列名称的SELECT语句进行查看,不能在队列中执行INSERT, UPDATE, 或者DELETE语句。保存在












 4 
队列中的消息即使重新启动服务器也不会丢失。    服务  
  服务程序是读取并处理队列中的消息的程序。这种服务可以是特定的存储程序,或者连接数据库的不同程序。每个服务必须与队列相关。正如先前所提到的,会话发生在服务之间。    会话组  
  会话组用来接连消息的处理过程并使之相互关联。每个会话是会话组中的一份子。主要的概念是有些消息与其他消息相关,会话组将这些相关的会话按照要求的顺序结合在一起。事实上,所进行的处理具有对会话组里的全部消息的高级连续访问权限,直到处理完成。  
  Service Broker 应用还有很多其他相关的部分。以上提到的各个部分在Service Broker起主要作用。您对它们越熟悉,您就会更熟练的掌握Service Broker应用的编写。现在,我们来看如何使用Service Broker应用来实现业务交易。  
  业务处理  
  业务流程中的任务很少按照同步进行。这些流程经常由彼此独立的任务组成,但是很可能同时发生,可能重叠,可能需要流程中别的步骤的支持。这种情况经常出现在生产产品的过程中,特别是客户订制的生产过程,比如汽车生产。    当有人预订一辆定制的汽车,汽车各个部件的生产过程并不彼此依赖。例如,这些部件可以同时生产。但是在最后阶段,当进行组装时你会遇到下面的问题:    ·取决于前一步骤的步骤。  
  ·如果出现错误会对整个项目的成功起绝对性影响的步骤。    ·需要购买者补充信息的步骤。  
  除了这种情况以外,如果潜在购买者取消了订单,那么进行补偿的程序也要符合逻辑。您可能对有类似特征的业务流程比较熟悉。  
  当数据库执行这样的流程时,经常按一系列数据库交易进行处理,每个交易都有单独的基本任务。当其中一个数据库交易被接受或退回时,这一系列相关的业务交易通常都无法以此方法完成。这些交易必须被设计成失败时,通过逻辑判断退回业务交易。整个业务程序都很难进行,因为这些独立的交易实际上是于彼此相关的,他们都包含同样的总体目标。这是Service Broker这样的队列结构的实际价值所在。  












 5 
  在Service Broker应用中,目前的处理方法是可能的,也经常是人们所需要的。您能够以这种方法建立应用模式,使模式更符合业务流程。在我们定制的汽车行业的例子中,您能够以这样的方式设计应用,使得跟踪底盘的模块和跟踪引擎的模块能够同时出现。更好的,对这两个独立的零件的处理通过对话组可以相互联系。

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
MySQL에 저장된 절차는 무엇입니까?MySQL에 저장된 절차는 무엇입니까?May 01, 2025 am 12:27 AM

저장된 절차는 성능을 향상시키고 복잡한 작업을 단순화하기 위해 MySQL에서 사전 컴파일 된 SQL 문입니다. 1. 성능 향상 : 첫 번째 편집 후 후속 통화를 다시 컴파일 할 필요가 없습니다. 2. 보안 향상 : 권한 제어를 통해 데이터 테이블 액세스를 제한합니다. 3. 복잡한 작업 단순화 : 여러 SQL 문을 결합하여 응용 프로그램 계층 로직을 단순화합니다.

쿼리 캐싱은 MySQL에서 어떻게 작동합니까?쿼리 캐싱은 MySQL에서 어떻게 작동합니까?May 01, 2025 am 12:26 AM

MySQL 쿼리 캐시의 작동 원리는 선택 쿼리 결과를 저장하는 것이며 동일한 쿼리가 다시 실행되면 캐시 된 결과가 직접 반환됩니다. 1) 쿼리 캐시는 데이터베이스 읽기 성능을 향상시키고 해시 값을 통해 캐시 된 결과를 찾습니다. 2) MySQL 구성 파일에서 간단한 구성, query_cache_type 및 query_cache_size를 설정합니다. 3) SQL_NO_CACHE 키워드를 사용하여 특정 쿼리의 캐시를 비활성화하십시오. 4) 고주파 업데이트 환경에서 쿼리 캐시는 성능 병목 현상을 유발할 수 있으며 매개 변수의 모니터링 및 조정을 통해 사용하기 위해 최적화해야합니다.

다른 관계형 데이터베이스를 통해 MySQL을 사용하면 어떤 장점이 있습니까?다른 관계형 데이터베이스를 통해 MySQL을 사용하면 어떤 장점이 있습니까?May 01, 2025 am 12:18 AM

MySQL이 다양한 프로젝트에서 널리 사용되는 이유에는 다음이 포함됩니다. 1. 고성능 및 확장 성, 여러 스토리지 엔진을 지원합니다. 2. 사용 및 유지 관리, 간단한 구성 및 풍부한 도구; 3. 많은 지역 사회 및 타사 도구 지원을 유치하는 풍부한 생태계; 4. 여러 운영 체제에 적합한 크로스 플랫폼 지원.

MySQL에서 데이터베이스 업그레이드를 어떻게 처리합니까?MySQL에서 데이터베이스 업그레이드를 어떻게 처리합니까?Apr 30, 2025 am 12:28 AM

MySQL 데이터베이스를 업그레이드하는 단계에는 다음이 포함됩니다. 1. 데이터베이스 백업, 2. 현재 MySQL 서비스 중지, 3. 새 버전의 MySQL 설치, 4. 새 버전의 MySQL 서비스 시작, 5. 데이터베이스 복구. 업그레이드 프로세스 중에 호환성 문제가 필요하며 Perconatoolkit과 같은 고급 도구를 테스트 및 최적화에 사용할 수 있습니다.

MySQL에 사용할 수있는 다른 백업 전략은 무엇입니까?MySQL에 사용할 수있는 다른 백업 전략은 무엇입니까?Apr 30, 2025 am 12:28 AM

MySQL 백업 정책에는 논리 백업, 물리적 백업, 증분 백업, 복제 기반 백업 및 클라우드 백업이 포함됩니다. 1. 논리 백업은 MySQLDump를 사용하여 데이터베이스 구조 및 데이터를 내보내며 소규모 데이터베이스 및 버전 마이그레이션에 적합합니다. 2. 물리적 백업은 데이터 파일을 복사하여 빠르고 포괄적이지만 데이터베이스 일관성이 필요합니다. 3. 증분 백업은 이진 로깅을 사용하여 변경 사항을 기록합니다. 이는 큰 데이터베이스에 적합합니다. 4. 복제 기반 백업은 서버에서 백업하여 생산 시스템에 미치는 영향을 줄입니다. 5. AmazonRDS와 같은 클라우드 백업은 자동화 솔루션을 제공하지만 비용과 제어를 고려해야합니다. 정책을 선택할 때 데이터베이스 크기, 가동 중지 시간 허용 오차, 복구 시간 및 복구 지점 목표를 고려해야합니다.

MySQL 클러스터링이란 무엇입니까?MySQL 클러스터링이란 무엇입니까?Apr 30, 2025 am 12:28 AM

mysqlclusteringenhancesdatabaserobustness andscalabilitydaturedingdataacrossmultiplenodes.itusesthendbenginefordatareplicationandfaulttolerance, highavailability를 보장합니다

MySQL의 성능을 위해 데이터베이스 스키마 설계를 어떻게 최적화합니까?MySQL의 성능을 위해 데이터베이스 스키마 설계를 어떻게 최적화합니까?Apr 30, 2025 am 12:27 AM

MySQL에서 데이터베이스 스키마 설계 최적화는 다음 단계를 통해 성능을 향상시킬 수 있습니다. 1. 인덱스 최적화 : 공통 쿼리 열에서 인덱스 생성, 쿼리의 오버 헤드 균형 및 업데이트 삽입. 2. 표 구조 최적화 : 정규화 또는 정상화를 통한 데이터 중복성을 줄이고 액세스 효율을 향상시킵니다. 3. 데이터 유형 선택 : 스토리지 공간을 줄이기 위해 Varchar 대신 Int와 같은 적절한 데이터 유형을 사용하십시오. 4. 분할 및 하위 테이블 : 대량 데이터 볼륨의 경우 파티션 및 하위 테이블을 사용하여 데이터를 분산시켜 쿼리 및 유지 보수 효율성을 향상시킵니다.

MySQL 성능을 어떻게 최적화 할 수 있습니까?MySQL 성능을 어떻게 최적화 할 수 있습니까?Apr 30, 2025 am 12:26 AM

tooptimizemysqlperformance, followthesesteps : 1) 구현 properIndexingToSpeedUpqueries, 2) useExplaintoAnalyzeanDoptimizeQueryPerformance, 3) AdvertServerConfigUrationSettingstingslikeInnodb_buffer_pool_sizeandmax_connections, 4) uspartOflEtOflEtOflestoI

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

mPDF

mPDF

mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경