SQLDependency_OnChange-Event仅触发一次

31

我正在使用SQLDependency来通知我数据库是否发生更改。 在程序启动后,它可以正常工作。当我进行第一次更改时,事件触发了。太好了...这很棒。 但是,如果我进行第二次更改,事件就不会再次触发。我已经搜索了整个网络,但似乎没有找到关于这个问题的任何信息。只能找到Onchange-Event在循环中触发的问题。 有人可以帮帮我吗?

以下是一小段示例代码:

private void GetStates()
    {
        if (!DoesUserHavePermission())
            return;

        SqlDependency.Stop(con);
        SqlDependency.Start(con);

        using (SqlConnection cn = new SqlConnection(con))
        {
            using (SqlCommand cmd = cn.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]"

                cmd.Notification = null;
                cmd.Dispose();

                SqlDependency dep = new SqlDependency(cmd);
                dep.OnChange += new OnChangeEventHandler(dep_OnChange);

                cn.Open();

                using (SqlDataReader dr = cmd.ExecuteReader())
                {
                    state.Clear(); //In this Case "state" is a List<string>
                    while (dr.Read())
                    {
                        state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
                    }
                    dr.Dispose();
                    dr.Close();
                }                    
            }
        }
    }

我的OnChange事件看起来像这样:

private void dep_OnChange(object sender, SqlNotificationEventArgs e)
    {
        SqlDependency dep = sender as SqlDependency;
        dep.OnChange -= this.dep_OnChange;

        using (SqlConnection cn = new SqlConnection(con))
        {
            using (SqlCommand cmd = cn.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "SELECT Bla, Bla2, ..FROM dbo.[BLA3]";

                cmd.Notification = null;

                if (e.Type == SqlNotificationType.Change)
                {
                    if (cn.State != ConnectionState.Open)
                    {
                        cn.Open();
                    }

                    using (SqlDataReader dr = cmd.ExecuteReader())
                    {
                        state.Clear(); // Clear and Refill the stringlist "state"
                        while (dr.Read())
                        {
                            state.Add(dr.GetString(0) + "|" + dr.GetInt32(3));
                        }
                    }
                }
                cn.Close();
            }
        }
        this.GetStates(); //to go ahead and wait for a new change
    }

问题出在哪里?


1
第一次事件调用后,您必须重新启动SqlDependency,这样它就不会在第二个事件等后续事件中断,而是可以正常工作。 - adnan
我有相同的问题,你能帮帮我吗? - vankhanhpr
6个回答

15

6

在首次对数据库进行更改后,您需要再次执行命令并重新订阅事件。

以下代码适用于我。

class Program
{
    static string connectionString = "Server=.;Database=test_sql_dependency;Integrated Security=True;";

    static void Main(string[] args)
    {
        // 1. create database

        // 2. enable service broker by executing this sql command on the database.
        // alter database test_sql_dependency set enable_broker

        // 3. start sql dependency, for some sql server connection string or with queue if you want.

        //var queueName = "myFirstQueue";
        //SqlDependency.Start(connectionString, queueName);
        SqlDependency.Start(connectionString);

        // complete the rest of the steps in seperate method to be able to call it again when you need to 
        // re-subscribe to the event again, becuase by default it will be executed only one time

        RegisterSqlDependency();

        Console.WriteLine("Listening to database changes...");
        Console.ReadLine();
    }

    static void RegisterSqlDependency()
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            if (connection.State != System.Data.ConnectionState.Open)
            {
                connection.Open();
            }

            // 4. create a sql command 
            // you can't say select *, and also you have to specefy the db owner (dbo.)
            SqlCommand command = new SqlCommand("select Id, Name from dbo.Employee", connection);

            // 5. create dependency and associtate it to the sql command
            SqlDependency dependency = new SqlDependency(command);

            // 6. subscribe to sql dependency event
            dependency.OnChange += new OnChangeEventHandler(OnDependencyChange);

            // 7. execute the command
            using (SqlDataReader reader = command.ExecuteReader())
            {

            }
        }
    }

    static void OnDependencyChange(object sender, SqlNotificationEventArgs e)
    {
        var InsertOrUpdateOrDelte = e.Info;



        //-----------------------------Finally-------------------------
        // after you knew that there is a change happened 
        // you have to unsubscribe the event and execute the command again and then re-subscribe to the event

        // 1. unsubscribe the event
        SqlDependency dependency = sender as SqlDependency;
        dependency.OnChange -= OnDependencyChange;

        // 2. re-subscribe to the event and execute the command again
        RegisterSqlDependency();

    }
}

我发现这种方法存在的问题是,如果在 onDependecyChange 调用之后、重新注册之前发生了数据库更改,那么这些更改将无法被检测到。这意味着这种方法不能保证对表中所有数据库更改进行通知。 - Marquez

3

在您的private void dep_OnChange(object sender, SqlNotificationEventArgs e)方法中,在取消订阅dep_OnChange事件之后,您应该再次调用private void GetStates(),以重新初始化dep.OnChange事件。


1
在 GetStates() 函数中:

SqlDependency.Stop(con); SqlDependency.Start(con);

当第一次注册 SQL 依赖关系时,这些行应该被执行。

当从 OnChange 事件中调用该方法时,请限制它们。


1

不确定那是否是你的问题,但你在创建完命令后立即将其处理掉了:

using (SqlCommand cmd = cn.CreateCommand()) 
{
  ...
  cmd.Dispose(); 

看起来像是一个 bug。


那个微软的示例代码做了同样的事情。我尝试了两种方法,但事件似乎仍然只触发了一次。 - BlueMonkMN

0

看这里,我的朋友:

dep.OnChange -= this.dep_OnChange;

你取消了事件的解雇;这是不正确的; 只需删除此行即可;


3
他需要这样做,因为每次都需要创建一个新的依赖项。这是一次性的变更事件。 - Marshal

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