当数据集发生更改时,SqlDependency不会触发OnChange事件。

4

我对 SQL Server 中的查询通知概念不熟悉,需要一些时间来理解。

我的目标是创建一个 Windows 服务应用程序,在 SQL Server 表格发生更改时得到通知。 我遵循了这个指南,它对我的起步有所帮助。

然而,我无法获得预期的结果。我的 Windows 服务应用程序中的 OnStart() 方法如下:

protected override void OnStart(string[] args)
{
        eventLog1.WriteEntry("Service Started");

        serviceRun = false;

        SqlClientPermission perm = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);

        try
        {
            perm.Demand();
            eventLog1.WriteEntry("permission granted");
        }
        catch (System.Exception)
        {
            eventLog1.WriteEntry("permission denied");
        }

        try
        {
            connstr = "Data Source=THSSERVER-LOCAL;Initial Catalog=ET;User ID=mujtaba;Password=ths123";

            connection = new SqlConnection(connstr);

            SqlCommand command = new SqlCommand("select * from dbo.Customer_FileUploads", connection);

            // Create a dependency and associate it with the SqlCommand.
            SqlDependency dependency = new SqlDependency(command);

            // Maintain the reference in a class member.
            // Subscribe to the SqlDependency event.
            dependency.OnChange += Dependency_OnChange;

            SqlDependency.Start(connstr);

            connection.Open();

            // Execute the command.
            using (SqlDataReader reader = command.ExecuteReader())
            {
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        //eventLog1.WriteEntry("reading data");
                    }
                }
                else
                {
                    eventLog1.WriteEntry("No rows found.");
                }
                reader.Close();
            }
        }
        catch (Exception e)
        {
            eventLog1.WriteEntry("Error Message: " + e.Message);
        }
}

事件SqlDependency的订阅方式如下:
private void Dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
    // Handle the event.
    eventLog1.WriteEntry("data changed");
}

OnStop()方法如下:

protected override void OnStop()
{
        SqlDependency.Stop(connstr);
        connection.Close();
        eventLog1.WriteEntry("In onStop.");
}

我的数据库中设置了ENABLE_BROKER为true。最终结果是,服务运行并创建了以下日志:

"Service Started"
"permission granted"
"data changed"

然而,当我向表中插入新数据时,OnChange() 事件不会触发,并且没有创建新日志。此外,当我停止并重新启动服务时, OnChange() 被触发,即使没有插入新数据。有人可以帮助我理解这个过程吗?

我也遇到了同样的问题,收到了无效的通知信息。原因是查询语句为“SELECT 1 FROM database.schema.table”,将查询语句中的数据库名称删除,改为“SELECT 1 FROM schema.table”即可解决问题。 - Tanner Ornelas
1个回答

16

SqlDependency在事件触发后被移除,因此您需要再次使用依赖项执行命令。以下是一个控制台应用程序示例,除非通知是由于错误引起的,否则将重新订阅。

using System;
using System.Data;
using System.Data.SqlClient;

namespace SqlDependencyExample
{
    class Program
    {

        static string connectionString = @"Data Source=.;Initial Catalog=YourDatabase;Application Name=SqlDependencyExample;Integrated Security=SSPI";

        static void Main(string[] args)
        {

            SqlDependency.Start(connectionString);

            getDataWithSqlDependency();

            Console.WriteLine("Waiting for data changes");
            Console.WriteLine("Press enter to quit");
            Console.ReadLine();

            SqlDependency.Stop(connectionString);

        }

        static DataTable getDataWithSqlDependency()
        {

            using (var connection = new SqlConnection(connectionString))
            using (var cmd = new SqlCommand("SELECT Col1, Col2, Col3 FROM dbo.MyTable;", connection))
            {

                var dt = new DataTable();

                // Create dependency for this command and add event handler
                var dependency = new SqlDependency(cmd);
                dependency.OnChange += new OnChangeEventHandler(onDependencyChange);

                // execute command to get data
                connection.Open();
                dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));

                return dt;

            }

        }

        // Handler method
        static void onDependencyChange(object sender,
           SqlNotificationEventArgs e)
        {

            Console.WriteLine($"OnChange Event fired. SqlNotificationEventArgs: Info={e.Info}, Source={e.Source}, Type={e.Type}.");

            if ((e.Info != SqlNotificationInfo.Invalid)
                && (e.Type != SqlNotificationType.Subscribe))
            {
                //resubscribe
                var dt = getDataWithSqlDependency();

                Console.WriteLine($"Data changed. {dt.Rows.Count} rows returned.");
            }
            else
            {
                Console.WriteLine("SqlDependency not restarted");
            }

        }


    }
}

6
这个例子比微软自己的例子简单得多,更易于理解。 - jaycer
我们是否应该在 onDependencyChange 中注销事件处理程序,因为每次事件发生时都会创建一个新的 SqlDependency 实例,并将处理程序添加到这个新实例中?即 if (sender is SqlDependency dependency) dependency.OnChange -= onDependencyChange; - Alex
@Alex,SqlDependency是短暂的。它不会再次触发,因为当事件触发时,处理程序被移除,并且所有QueryNotification相关的东西也被拆除了。我认为明确删除处理程序没有任何伤害,因为它是一个无操作。 - Dan Guzman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接