SQL Server Service Broker 通知队列在等待 SQL 查询后不再触发
SQL Server Service Broker notification queue dont fire anymore after waiting for it with a SQL Query
(语法错误警告)
我想在每次特定的 table 发生变化时得到通知,比如插入了新行。只要我不尝试用查询而不是我的程序来监视 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;
队列停止工作。我的程序正在等待通知,但是当我向 table.
中插入内容时,SQL 服务器不再创建通知
当我像这样检查队列是否处于活动状态时:
select is_receive_enabled
from sys.service_queues
where name = N'SQLDependencyQueue';
它显示“1”表示活动,但仍然没有收到通知。
只有我重新启动程序并再次订阅队列,它才能再次工作。
编辑:
查看订阅时
select * from sys.dm_qn_subscriptions
程序启动时有两个条目(无需手动等待)。当我还额外手动等待通知时,我以为我得到了 3 行,但是程序停止工作并且在订阅中 table 只剩下一行,即使我停止了手动等待。
如果您 运行 手动 RECEIVE
并收到一条消息,则表示您已使用该通知。在您的 C# 代码中,在使用通知后您 再次订阅 。如果您从 SSMS 使用它,那么您的程序将不会再次订阅,因此您最终会看到程序永远等待已经从队列中使用的通知,并且没有人会重新订阅。
您可以检查sys.dm_qn_subscriptions
查询通知的状态订阅。
大多数人对查询通知怀念的是,您只会收到 一个 通知,然后您必须重新订阅。许多人希望订阅一次,然后在每次更改时收到一系列通知。该功能专为缓存失效而设计(您通过查询获取缓存中的数据,然后在刷新缓存时收到通知)。它不适用于监视 table 的更改(这似乎是您正在尝试的)。
(语法错误警告) 我想在每次特定的 table 发生变化时得到通知,比如插入了新行。只要我不尝试用查询而不是我的程序来监视 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;
队列停止工作。我的程序正在等待通知,但是当我向 table.
中插入内容时,SQL 服务器不再创建通知当我像这样检查队列是否处于活动状态时:
select is_receive_enabled
from sys.service_queues
where name = N'SQLDependencyQueue';
它显示“1”表示活动,但仍然没有收到通知。
只有我重新启动程序并再次订阅队列,它才能再次工作。
编辑:
查看订阅时
select * from sys.dm_qn_subscriptions
程序启动时有两个条目(无需手动等待)。当我还额外手动等待通知时,我以为我得到了 3 行,但是程序停止工作并且在订阅中 table 只剩下一行,即使我停止了手动等待。
如果您 运行 手动 RECEIVE
并收到一条消息,则表示您已使用该通知。在您的 C# 代码中,在使用通知后您 再次订阅 。如果您从 SSMS 使用它,那么您的程序将不会再次订阅,因此您最终会看到程序永远等待已经从队列中使用的通知,并且没有人会重新订阅。
您可以检查sys.dm_qn_subscriptions
查询通知的状态订阅。
大多数人对查询通知怀念的是,您只会收到 一个 通知,然后您必须重新订阅。许多人希望订阅一次,然后在每次更改时收到一系列通知。该功能专为缓存失效而设计(您通过查询获取缓存中的数据,然后在刷新缓存时收到通知)。它不适用于监视 table 的更改(这似乎是您正在尝试的)。