SqlDependency.OnChange firing but SqlDataReader is not returning with data

1.9k views Asked by At

When I execute a query with a datetime column filter

WHERE [Order].CreatedOn >= @CreatedOn

using a SqlDependency, the change on data source fires the SqlDependency.OnChange event but the SqlDataReader associated with the SqlCommand doesn't return data (reader.HasRows always returns false).

When I just change the filter condition in my SQL statement to

WHERE [Order].StatusId = 1"

it just works fine and the SqlDataReader returns data (reader.HasRows returns true)

Code:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace SignalRServer
{
    public partial class DepartmentScreen : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var u = System.Security.Principal.WindowsIdentity.GetCurrent().User;
            var UserName = u.Translate(Type.GetType("System.Security.Principal.NTAccount")).Value;

            CheckForNewOrders(DateTime.Now);
        }

        private void CheckForNewOrders(DateTime dt)
        {
            string json = null;
            string conStr = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;

            using (SqlConnection connection = new SqlConnection(conStr))
            {
                string query = string.Format(@"
                        SELECT [Order].OrderId
                        FROM [dbo].[Order]
                        WHERE [Order].CreatedOn >= @CreatedOn");

                //                query = string.Format(@"
                //                        SELECT [Order].OrderId
                //                        FROM [dbo].[Order]
                //                        WHERE [Order].StatusId = 1");

                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    command.Parameters.Add("@CreatedOn", SqlDbType.DateTime);
                    command.Parameters["@CreatedOn"].Value = DateTime.Now;

                    command.Notification = null;
                    SqlDependency dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                    connection.Open();
                    SqlDataReader reader = command.ExecuteReader();

                    if (reader.HasRows)
                    {
                        reader.Read();
                        json = reader[0].ToString();
                    }
                }
            }

            SignalRHub hub = new SignalRHub();
            hub.OrderReceived(json, null);
        }

        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                CheckForNewOrders(DateTime.Now);
            }
            else
            {
                //Do somthing here
                //Console.WriteLine(e.Type);
            }
        }
    }
}

Images:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

2

There are 2 answers

0
Fabian On BEST ANSWER

When passing DateTime.Now as a reference Date you are not very likely to retrieve records that have been created at some point in time (except if the records are created in the future and therefore you have some problem with your server time or the column name "createdOn" is very missleading).

To get the latest records based on some update date you need to do something like this:

  • create a global variable that saves the max created date that you have already retrieved (_refDate in my example, initialized to the value you choose, DateTime.MinValue in my case to get all records in the first call and then only get them incrementally, you can also take DateTime.Now to start at one moment in time)
  • trigger the CheckForNewOrders query
  • when you retrieve the results also send the CreatedOn column and save the maximum retrieved CreatedOn date as the new reference date
  • when the value changes in DB and the dependency_OnChange event gets triggered you need to trigger the query with the last value of _refDate in order to get everything you haven't retrieved yet
  • update the value of _refDate again and so on ..

Not tested but this should work (take care of _refDate to be acccessible globally)

public partial class DepartmentScreen : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var u = System.Security.Principal.WindowsIdentity.GetCurrent().User;
            var UserName = u.Translate(Type.GetType("System.Security.Principal.NTAccount")).Value;

            CheckForNewOrders(_refDate);
        }

        private DateTime _refDate = DateTime.MinValue;

        private void CheckForNewOrders(DateTime dt)
        {
            string json = null;
            string conStr = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;

            using (SqlConnection connection = new SqlConnection(conStr))
            {
                string query = string.Format(@"
                    SELECT [Order].OrderId, [Order].CreatedOn
                    FROM [dbo].[Order]
                    WHERE [Order].CreatedOn >= @CreatedOn");

                //                query = string.Format(@"
                //                        SELECT [Order].OrderId
                //                        FROM [dbo].[Order]
                //                        WHERE [Order].StatusId = 1");

                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    command.Parameters.Add("@CreatedOn", SqlDbType.DateTime);
                    command.Parameters["@CreatedOn"].Value = dt;

                    command.Notification = null;
                    SqlDependency dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                    connection.Open();
                    SqlDataReader reader = command.ExecuteReader();

                    if (reader.HasRows)
                    {
                        while (reader.Read())
                        {
                            //json = reader[0].ToString();
                            var date = Convert.ToDateTime(reader["CreatedOn"]);

                            if (date > _refDate)
                            {
                                _refDate = date;
                            }
                        }
                    }
                }
            }

            //SignalRHub hub = new SignalRHub();
            //hub.OrderReceived(json, null);
        }

        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                CheckForNewOrders(_refDate);
            }
            else
            {
                //Do somthing here
                //Console.WriteLine(e.Type);
            }
        }
    }
}
3
Chanakya On

When the method CheckForNewOrders is called from onchange event

command.Parameters.Add("@CreatedOn", SqlDbType.DateTime);
command.Parameters["@CreatedOn"].Value = DateTime.Now;

For the parameter @CreatedOn you are passing DateTime.Now (Not the time of its change). There will not be any data satisfying the condition in database.