【问题标题】:SQL Server Service Broker notification queue dont fire anymore after waiting for it with a SQL QuerySQL Server Service Broker 通知队列在使用 SQL 查询等待它后不再触发
【发布时间】:2023-03-05 12:59:01
【问题描述】:

(语法错误警告) 我想在每次特定表发生变化时得到通知,比如插入新行。只要我不尝试使用查询而不是我的程序来监视 SQL Server/SSMS 中的队列,它就可以工作。

我像这样创建了一个新的服务和队列:

CREATE QUEUE SQLDependencyQueue;
CREATE SERVICE NamesService
ON QUEUE SQLDependencyQueue
([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]) ;

当我现在用一个小 C# 程序听队列时,它就像一个魅力。

 private void InitializGetData()
        {
            String connectionString = "Server=DESKTOP-N943R2B\\SQLEXPRESS01;Database = DBDependency; User Id = sa;Password = admin; ";

            if (conn == null)
            {
                conn = new SqlConnection(connectionString);
            }

            if(command == null)
            {
                command = new SqlCommand(@"SELECT[id],[lade_nr],[vg_lfd_nr],[status] FROM[dbo].[t3_auftr_log_ll] where [status] = 0", conn);
            }

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

            notification = new SqlNotificationRequestRegister("WAITFOR(RECEIVE * FROM SQLDependencyQueue);", strServiceName, timeOut, connectionString);
            notification.OnChanged += NotificationOnChanged;

            getData();
        }

        void getData()
        {
            if(notification == null)
            {
                return;
            }

            dataToWatch.Clear();

            command.Notification = null;
            command.Notification = notification.NotificationRequest;

            //notification.StartSqlNotification();

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

                for(int i= 0; i <= dataToWatch.Tables["Order_log_ll"].Rows.Count - 1; i++)
                {
                    Console.WriteLine(String.Format("Row: {0}", dataToWatch.Tables["t3_Auftr_log_ll"].Rows[i]["vg_lfd_nr"].ToString())); //, dataToWatch.Tables["t3_Auftr_log_ll"].Rows[i]["status"].ToString()

                    using (SqlCommand cmd = new SqlCommand("update t3_auftr_log_ll set status = 1 where id = @id",conn))
                    {
                        if (conn.State != System.Data.ConnectionState.Open)
                        {
                            conn.Open();
                        }
                        cmd.Parameters.AddWithValue("@id", dataToWatch.Tables["t3_Auftr_log_ll"].Rows[i]["id"].ToString());
                        cmd.ExecuteNonQuery();
                    }
                }
            }

            notification.StartSqlNotification();
        }
private void RegisterSqlNotificationRequest()
        {
            request = new SqlNotificationRequest();
            request.UserData = new Guid().ToString();
            request.Options = String.Format("Service={0}", strServiceName);
            request.Timeout = intNotificationTimeout;

            if (OnChanged != null)
            {
                OnChanged(this, null);
            }
        }
public void Listen()
        {
            using (SqlConnection conn = new SqlConnection(strConnectionString))
            {
                using (cmd = new SqlCommand("WAITFOR(RECEIVE * FROM SQLDependencyQueue);", conn))
                {
                    if (conn.State != System.Data.ConnectionState.Open)
                    {
                        conn.Open();
                    }

                    cmd.CommandTimeout = intNotificationTimeout + 120;

                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            for (int i = 0; i <= reader.FieldCount - 1; i++)
                                Debug.WriteLine(reader[i].ToString());
                        }
                    }
                }
            }

            RegisterSqlNotificationRequest();
        }

但是如果我启动程序然后在 SSMS 中调用一个查询来查看队列中的内容:

WAITFOR(RECEIVE * FROM SQLDependencyQueue);

或类似:

RECEIVE conversation_handle, message_type_name, message_body  
FROM SQLDependencyQueue;

队列停止工作。我的程序正在等待通知,但是当我向表中插入某些内容时,SQL 服务器不再创建通知。

当我像这样检查队列是否处于活动状态时:

select is_receive_enabled
from sys.service_queues
where name = N'SQLDependencyQueue';

它显示“1”表示活动,但仍然没有收到通知。

只有当我重新启动程序并再次订阅队列时,它才能再次工作。

编辑:

查看订阅时

select * from sys.dm_qn_subscriptions

程序启动时有两个入口(无需手动等待)。当我还额外手动等待通知时,我以为我得到了 3 行,但是程序停止工作并且在订阅表中只剩下一行,即使我停止了手动等待。

【问题讨论】:

    标签: c# sql-server service-broker


    【解决方案1】:

    如果您手动运行RECEIVE 并收到一条消息,则表示您已使用该通知。在您的 C# 代码中,在使用通知后,您再次订阅。如果您从 SSMS 消费它,那么您的程序将不会再次订阅,因此您最终会导致程序永远等待已经从队列中消费的通知,并且没有人会重新订阅。

    您可以查看sys.dm_qn_subscriptions 了解您的查询通知状态订阅

    大多数人对查询通知的怀念是您只会收到一个通知,然后您必须再次订阅。许多人希望订阅一次,然后每次更改都会收到通知流。该功能专为缓存失效而设计(您通过查询获取缓存中的数据,然后在刷新缓存时收到通知)。它不适用于监视表的更改(您似乎正在尝试这样做)。

    【讨论】:

    • 添加可以使用SELECT 查询而不是RECEIVE 来检查SSMS 队列中的消息,以避免使用通知。
    • 为什么不能监控?我的意思是,如果我只是让程序运行而不自己在 SSMS 中接收,它就像我期待的那样工作。每次桌子发生变化时我都会收到通知。
    • 我注意到的是,当程序运行时,我在该表中有两行,当它停止工作时,因为我手动等待通知,我只有一行,即使我停止手动等待它。 (将其添加到我的原始帖子中)
    • 它不起作用,因为您可能会在收到通知后错过通知,直到您再次订阅。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    相关资源
    最近更新 更多