1

i am monitoring a database table for data change like insert/Update. some sql dependency throw error i heard like when no activity is there for a specific table for long days like 3/4 days. i am trying to say if i am monitoring a table and if no data inserted or updated in that table for long time then SQL Dependency throw error. here is my code. please see my code and tell me am i handing the error properly.

my sql dependency code is written in windows service. i want if any error is thrown then my windows service should restart. here is my full code

 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
    }

see this function because if error occur then i am restarting service from here by this code 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();
        }

the problem is my service is not sending any mail when data change in specific table but if i restart my service then service is sending mail when data is changed in specific table which is monitoring by the sql dependency. need help.

Mou
  • 15,673
  • 43
  • 156
  • 275

2 Answers2

1

EDIT [Removed my comment regarding fully qualifying the table]

At first glance your code seems fine. You are registering the command and executing it. I would change it to execute only once instead of many times

After registering the dependency put a break point and then execute sys.dm_qn_subscriptions. If it's registered pay attention to the statuscolumn and look here for its meaning

If it's not registered execute sys.transmission_queue to diagnose.

Here it goes my SP cheat sheet for subscriptions

  • sys.dm_qn_subscriptions ==> Retrieve the active subscriptions
  • sys.internal_tables ==> how's the space used by query notification subscriptions that are currently active.
  • sys.transmission_queue ==> troubleshoot notifications
Luis Filipe
  • 8,488
  • 7
  • 48
  • 76
  • [bba-reman] is the owner name and that is why i wrote it like [bba-reman].MyLog – Mou Mar 30 '15 at 12:07
  • can u tell me am i handing problem properly in this function OnDataChange ? – Mou Mar 30 '15 at 12:08
  • when i start the win service including sql dependency then i saw it is working properly for approx 10 days and after that service run but no mail comes to me when data change in table. i am not being able to capture the shuttle problem in code – Mou Mar 30 '15 at 12:10
1

Be careful using SqlDependency class - it has the problems with memory leaks. Hovewer, you can use an open source realization of the SqlDependency class - SqlDependencyEx. It uses a database trigger and native Service Broker notification to receive events about the table changes. This is an usage example:

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);

With SqlDependecyEx you are able to monitor INSERT, DELETE, UPDATE separately and receive actual changed data (xml) in the event args object. Hope this help.

Community
  • 1
  • 1
dyatchenko
  • 2,283
  • 3
  • 22
  • 32