实现数据库变更通知II

2013-09-07  籽藤 

三年前,我自己搞了个工具,去监视数据库中数据的变更(/blog/7428)。最近为了便于当前项目的使用,我又搞了这个所谓“数据库变更通知II”。

源码下载地址:http://download.csdn.net/detail/wuziteng2006/6228703

这两个版本的使用背景完全不一样。第一个版本是为了获得异常信息,因为程序抛出的异常、错误都存在某个数据表中,所以我的工具只要监控这张表,一旦发现有新数据,就直接发邮件通知相关人员;而第二个版本,不仅是监控一张表,而是数据库的所有表;因为程序调用了大量的Web Service,大部分都是其他Team提供的API,我们对这些API具体做了哪些事情,对哪个数据库的哪张表做了Insert,Update,Delete操作都不了解,平时只能手动去查。当然,我们通过SQL Profile也可以了解程序后台的SQL处理,但是对于存储过程来说,SQL Profile并不直观,它会显示exec了某个存储过程,而后我们还得费心去阅读代码,才能真切地了解这个存储过程做了哪些动作...因此,为了更好地理解API,‘数据库变更通知II’我也叫它DBMonitor就应运而生了。



遇到的问题

1. TimeOut 超时异常
跟第一版一样,数据表变更通知的核心是SqlDependency,所以需要对监控的数据库设置Service Broker,如:
ALTER DATABASE SampleDb SET ENABLE_BROKER
如果遇到了TimeOut异常,就关闭SQL Server的Agent代理功能,再执行语句。在确认Broker状态为Enable之后可启动Agent。
SELECT NAME, IS_BROKER_ENABLED FROM SYS.DATABASES

BTW, 网上有人建议用
sqlCacheDependency或是AggregateCacheDependency来取代SqlDependency;前两者我没试过,反正SqlDependency就用着挺好的

2. The Service Broker in database "DACustomerDB" cannot be enabled because there is already an enabled Service Broker with the same ID
项目的测试环境非常复杂,很多核心数据库都有两个Partition,比如Subscription1和Subscription2。举个例子,我们在测试机上调用CreateCustomer接口,它会随机在Partition中两者择其一,要么在
Subscription1上插入数据,要么影响了Subscription2。所以,如果要了解CreateCustomer,我们要把Subscription1和2都设置Broker。但如果Subscription1已经设置了,那么使用SET ENABLE_BROKER对Subscription2进行操作时,会出现上述错误。
解决办法是:
Alter database DACustomerDB set NEW_BROKER
详见http://www.symantec.com/business/support/index?page=content&id=TECH181848

3.
INSERT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
可能是SQL Server本身Query Notification设计的原因,如果存储过程的QUOTED_IDENTIFIER设置为OFF,就无法捕获到它对数据表造成的变更。为了解决这个问题,我只得把存储过程的内容导出,把QUOTED_IDENTIFIER设置为ON。当然,设置为ON之前,我已经测试过,这样改动不会影响程序功能。以下是实现方法:

 public int HackSP4Notification()
        {

            IList<string> StoreProce = new List<string>();
            StringBuilder strTemp = new StringBuilder();

            string strSQL = @"
                select b.[name],a.[text] , replace(a.[text],'CREATE PROCEDURE','ALTER PROCEDURE') as NewText from syscomments A inner join sysobjects B on A.ID=B.ID
                where b.xtype='P'
                and name in (
                select s.name from sys.objects s
                    where
                    OBJECTPROPERTY(s.object_id,'ExecIsQuotedIdentOn')=0
                )
                ";
            try
            {
                DataTable dt = this.GetDataTable(strSQL);
               
                //Get all of Store_Procedure Name
                var spNames = (from row in dt.AsEnumerable() select row.Field<string>("Name")).Distinct();

                foreach (var sp in spNames) {

                    StoreProce.Add(sp);

                    //Query the detailed script by sp name
                    var spText = from r in dt.AsEnumerable()
                                 where r.Field<string>("name") == sp
                                 select r.Field<string>("NewText");

                    //Insert the sp text into a string
                    foreach (var subText in spText) {

                        strTemp.Append(subText.ToString());
                    }

                    //Execute the sql command to ensure "SET QUOTED_IDENTIFIER ON"
                    this.ExecuteSQLStmt(strTemp.ToString());
                    strTemp.Clear();
                }

            }
            catch (Exception)
            {
                throw new Exception(strTemp.ToString());
            }

            return StoreProce.Count;
        }

445°/4456 人阅读/0 条评论 发表评论

登录 后发表评论