如何正确处理SQL依赖项错误

时间:2015-03-30 11:15:54

标签: c# sql-server sqldependency

我正在监视数据库表以进行数据更改,例如insert / Update。一些sql依赖项抛出错误我听说就像3/4天这样长时间没有特定表的活动。我试图说,如果我正在监视一个表,如果该表中没有插入或更新数据很长时间,那么SQL依赖关系抛出错误。 这是我的代码。请查看我的代码并告诉我正确处理错误。

我的sql依赖代码是用windows服务编写的。我想如果抛出任何错误,那么我的Windows服务应该重新启动。这是我的完整代码

 public partial class PartIndexer : ServiceBase
    {
        static string connectionString = "server=222;uid=222;password=222;database=wwww;Pooling=true;Connect Timeout=20;";
        SqlDependency dep;

        public PartIndexer()
        {
            InitializeComponent();
        }

        #region OnStart
        protected override void OnStart(string[] args)
        {
            RegisterNotification();
            MailNotify("STARTED");
        }
        #endregion

        #region RegisterNotification
        /// <summary>
        /// RegisterNotification
        /// this is main routine which will monitor data change in ContentChangeLog table
        /// </summary>
        private void RegisterNotification()
        {
            string tmpdata = "";
            System.Data.SqlClient.SqlDependency.Stop(connectionString);
            System.Data.SqlClient.SqlDependency.Start(connectionString);

            try
            {
                using (SqlConnection conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                    SqlCommand cmd = conn.CreateCommand();
                    cmd.CommandText = "SELECT ActivityDate FROM [bba-reman].MyLog";
                    dep = new SqlDependency(cmd);
                    dep.OnChange += new OnChangeEventHandler(OnDataChange);
                    SqlDataReader dr = cmd.ExecuteReader();
                    {
                        while (dr.Read())
                        {
                            if (dr[0] != DBNull.Value)
                            {
                                tmpdata = dr[0].ToString();
                            }
                        }
                    }
                    dr.Dispose();
                    cmd.Dispose();
                }
            }
            finally
            {
                //SqlDependency.Stop(connStr);
            }

        }
        #endregion

        // this below function never used
        public void ReStartService()
        {
            ServiceController service = new ServiceController("PartIndexer");

            if ((service.Status.Equals(ServiceControllerStatus.Stopped)) || (service.Status.Equals(ServiceControllerStatus.StopPending)))
            {
                service.Start();
            }
            else
            {
                service.Stop();
                service.WaitForStatus(ServiceControllerStatus.Stopped);
                service.Start();
                service.WaitForStatus(ServiceControllerStatus.Running);
            }
        }

        #region OnDataChange
        /// <summary>
        /// OnDataChange
        /// OnDataChange will fire when after data change found in ContentChangeLog table
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnDataChange(object sender, SqlNotificationEventArgs e)
        {
            ((SqlDependency)sender).OnChange -= OnDataChange;

            if (e.Source == SqlNotificationSource.Timeout)
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer Service Exception Timeout occur " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Service Exception Timeout occur")
                    .WithSender("xxx@xxx.com")
                    .WithRecepient("xxx@xxx.com")
                    .Send();
                Environment.Exit(1);
                return;
            }
            else if (e.Source != SqlNotificationSource.Data)
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer Service Exception SqlNotificationSource.Data " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Service Exception SqlNotificationSource.Data")
                    .WithSender("xxx@xxx.com")
                    .WithRecepient("xxx@xxx.com")
                    .Send();

                Environment.Exit(1);
            }

            StartIndex();
            RegisterNotification();
        }
         #endregion

        #region StartIndex
        /// <summary>
        /// StartIndex
        /// this routine will call web service in bba reman website which will invoke routine to re-index data
        /// </summary>
        void StartIndex()
        {
            //eventLog1.WriteEntry("Web Service called start for indexing data"); 

            PartIndexerWS.AuthHeader oAuth = new PartIndexerWS.AuthHeader();
            oAuth.Username = "Admin";
            oAuth.Password = "Admin";

            PartIndexerWS.SearchDataIndex DataIndex = new PartIndexerWS.SearchDataIndex();
            DataIndex.AuthHeaderValue = oAuth;
            try
            {
                DataIndex.StartIndex();
                //eventLog1.WriteEntry("Web Service called stop for indexing data"); 
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.Message.ToString());
                //eventLog1.WriteEntry("Web Service call error "+ex.Message.ToString()); 

            }

        }
        #endregion

        #region MailNotify
        /// <summary>
        /// MailNotify
        /// fire mail when apps start & exit
        /// </summary>
        /// <param name="strStatus"></param>
        void MailNotify(string strStatus)
        {
            if (strStatus == "STARTED")
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer Started Date " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Started")
                    .WithSender("xxx@xxx.com")
                    .WithRecepient("xxx@xxx.com")
                    .Send();
                //eventLog1.WriteEntry("mail fired "); 

            }
            else if (strStatus == "STOPPED")
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer stopped Date " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Stopped")
                    .WithSender("xxx@xxx.com")
                    .WithRecepient("xxx@xxx.com")
                    .Send();
                //eventLog1.WriteEntry("mail fired "); 
            }
        }
        #endregion

        #region OnStop
        protected override void OnStop()
        {
            System.Data.SqlClient.SqlDependency.Stop(connectionString);
            MailNotify("STOPPED");
            //eventLog1.WriteEntry("Part Indexer stopped Date : " + DateTime.Now.ToLongDateString()); 

        }
        #endregion
    }

看到此函数,因为如果发生错误,我将通过此代码Environment.Exit(1);从此处重新启动服务。

void OnDataChange(object sender, SqlNotificationEventArgs e)
        {
            ((SqlDependency)sender).OnChange -= OnDataChange;

            if (e.Source == SqlNotificationSource.Timeout)
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer Service Exception Timeout occur " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Service Exception Timeout occur")
                    .WithSender("xxx@xxx.com")
                    .WithRecepient("xxx@xxx.com")
                    .Send();
                Environment.Exit(1);
                return;
            }
            else if (e.Source != SqlNotificationSource.Data)
            {
                var template = new MailTemplate()
                    .WithBody("HI,<br><br>Part Indexer Service Exception SqlNotificationSource.Data " + DateTime.Now.ToLongDateString())
                    .WithSubject("Part Indexer Service Exception SqlNotificationSource.Data")
                     .WithSender("xxx@xxx.com")
                     .WithRecepient("xxx@xxx.com")
                     .Send();

                Environment.Exit(1);
            }

            StartIndex();
            RegisterNotification();
        }

问题是我的服务在特定表中的数据更改时没有发送任何邮件,但是如果我重新启动我的服务,那么当在由sql依赖项监视的特定表中更改数据时,服务正在发送邮件。需要帮助。

2 个答案:

答案 0 :(得分:1)

修改 [删除了关于完全限定表格的评论]

乍一看,您的代码似乎很好。您正在注册该命令并执行它。 我会将其更改为仅执行一次而不是多次执行

注册依赖项后,设置一个断点,然后执行sys.dm_qn_subscriptions。 如果已注册,请注意status列,并查看here的含义

如果未注册执行sys.transmission_queue进行诊断。

这是我订阅的SP备忘单

  • sys.dm_qn_subscriptions ==&gt;检索有效订阅
  • sys.internal_tables ==&gt;如何使用当前活动的查询通知订阅所用的空间。
  • sys.transmission_queue ==&gt;排除通知疑难解答

答案 1 :(得分:1)

小心使用SqlDependency类 - 它有problems内存泄漏。 Hovewer,您可以使用SqlDependency类的开源实现 - SqlDependencyEx。它使用数据库触发器和本机Service Broker通知来接收有关表更改的事件。这是一个用法示例:

int changesReceived = 0;
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
          TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME)) 
{
    sqlDependency.TableChanged += (o, e) => changesReceived++;
    sqlDependency.Start();

    // Make table changes.
    MakeTableInsertDeleteChanges(changesCount);

    // Wait a little bit to receive all changes.
    Thread.Sleep(1000);
}

Assert.AreEqual(changesCount, changesReceived);

使用SqlDependecyEx,您可以单独监视INSERT,DELETE,UPDATE并在事件args对象中接收实际更改的数据(xml)。希望这有帮助。