Unstable application uses SqlDependency. Several states and errors

337 Views Asked by At

I have a windows application using SqlDependency running at separated thread pool, this application represents a log monitor UI get the latest rows added in a specific table in the database and view it in a DataGridView. You can see the application source code from this LINK, or follow this script.

    const string tableName = "OutgoingLog";
    const string statusMessage = "{0} changes have occurred.";
    int changeCount = 0;

    private static DataSet dataToWatch = null;
    private static SqlConnection connection = null;
    private static SqlCommand command = null;

    public frmMain()
    {
        InitializeComponent();
    }

    private bool CanRequestNotifications()
    {
        // In order to use the callback feature of the
        // SqlDependency, the application must have
        // the SqlClientPermission permission.
        try
        {
            SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);

            perm.Demand();

            return true;
        }
        catch
        {
            return false;
        }
    }

    private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
    {
        // This event will occur on a thread pool thread.
        // Updating the UI from a worker thread is not permitted.
        // The following code checks to see if it is safe to
        // update the UI.
        ISynchronizeInvoke i = (ISynchronizeInvoke)this;

        // If InvokeRequired returns True, the code
        // is executing on a worker thread.
        if (i.InvokeRequired)
        {
            // Create a delegate to perform the thread switch.
            OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);

            object[] args = { sender, e };

            // Marshal the data from the worker thread
            // to the UI thread.
            i.BeginInvoke(tempDelegate, args);

            return;
        }

        // Remove the handler, since it is only good
        // for a single notification.
        SqlDependency dependency = (SqlDependency)sender;

        dependency.OnChange -= dependency_OnChange;

        // At this point, the code is executing on the
        // UI thread, so it is safe to update the UI.
        ++changeCount;
        lblChanges.Text = String.Format(statusMessage, changeCount);

        // Reload the dataset that is bound to the grid.
        GetData();
    }

    AutoResetEvent running = new AutoResetEvent(true);

    private void GetData()
    {
        // Start the retrieval of data on another thread to let the UI thread free
        ThreadPool.QueueUserWorkItem(o =>
        {
            running.WaitOne();

            // Empty the dataset so that there is only
            // one batch of data displayed.
            dataToWatch.Clear();

            // Make sure the command object does not already have
            // a notification object associated with it.
            command.Notification = null;

            // Create and bind the SqlDependency object
            // to the command object.
            SqlDependency dependency = new SqlDependency(command);

            dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);

            using (SqlDataAdapter adapter = new SqlDataAdapter(command))
            {
                adapter.Fill(dataToWatch, tableName);



                try
                {
                    running.Set();
                }
                finally
                {
                    // Update the UI
                    dgv.Invoke(new Action(() =>
                    {
                        dgv.DataSource = dataToWatch;
                        dgv.DataMember = tableName;

                        //dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
                    }));
                }


            }
        });
    }

    private void btnAction_Click(object sender, EventArgs e)
    {
        changeCount = 0;
        lblChanges.Text = String.Format(statusMessage, changeCount);

        // Remove any existing dependency connection, then create a new one.
        SqlDependency.Stop("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
        SqlDependency.Start("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");

        if (connection == null)
        {
            connection = new SqlConnection("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");

        }

        if (command == null)
        {
            command = new SqlCommand("select * from OutgoingLog", connection);

            //SqlParameter prm =
            //    new SqlParameter("@Quantity", SqlDbType.Int);
            //prm.Direction = ParameterDirection.Input;
            //prm.DbType = DbType.Int32;
            //prm.Value = 100;
            //command.Parameters.Add(prm);
        }

        if (dataToWatch == null)
        {
            dataToWatch = new DataSet();
        }

        GetData();
    }

    private void frmMain_Load(object sender, EventArgs e)
    {
        btnAction.Enabled = CanRequestNotifications();
    }

    private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
    {
        SqlDependency.Stop("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
    }

The problem: I have many situations of errors, (images in the first comment)

(No. 1): I got this error dialog, and I don't know its reason.

(No. 2): I got nothing in my grid view (No errors, and no data).

(No. 3): I got only columns names and no rows, although the table has rows.

I need help please.

1

There are 1 best solutions below

5
On

I may be wrong but a DataSet does not seem to have notification capability so the DataGridView may be surprised if you change it behind its back.

You could try to explicitly show your're changing the data source by first setting it to null:

dgv.DataSource = null;
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;

It's worth a try...