无法使用 SqlDependency 接收更改通知
Cannot receive change notifications using SqlDependency
我是第一次使用 Sql Server Service Broker,尝试在特定 table 发生变化时订阅通知。当我调用 command.ExecuteReader()
:
时出现以下异常
System.InvalidOperationException : When using SqlDependency without providing an options value, SqlDependency.Start() must be called prior to execution of a command added to the SqlDependency instance.
我已经创建了一个测试来重现该场景(为简洁起见省略了一些相关方法),如下所示:
private string _queueName = "EventsToPublishChangeMessages";
private bool _notificationReceived;
[Test]
public void WhyDoesExceptionIndcateSqlDependencyStartHasNotBeenCalledPriorToCommandExecuteReader()
{
Console.WriteLine($"canRequestNotifications: {CanRequestNotifications()}"); // returns true
var connectionString = GetConnectionString();
var started = SqlDependency.Start(connectionString, _queueName); // exception below seems to suggest that I haven't started the SqlDependency. Am I doing something wrong on this line?
Console.WriteLine($"Started:{started}"); // returns true
var connection = new SqlConnection(connectionString);
var command = new SqlCommand("SELECT Id, EventType, [Data], Created FROM dbo.EventsToPublish", connection);
var sqlDependency = new SqlDependency(command);
sqlDependency.OnChange += OnChange;
connection.Open();
// The following line causes the exception:
// System.InvalidOperationException : When using SqlDependency without providing an options value, SqlDependency.Start() must be called prior to execution of a command added to the SqlDependency instance.
// But you can see that I *did* call SqlDependency.Start() above.
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Process(reader);
}
}
TryToTriggerANotification(connectionString);
Thread.Sleep(5000); // ie. wait a few seconds to ensure notification-handling background thread has had a chance to complete.
Assert.That(_notificationReceived, Is.True);
}
private void TryToTriggerANotification(string connectionString)
{
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(
"INSERT INTO dbo.EventsToPublish(Id, EventType, [Data], Created) VALUES(newid(), 'WhosebugQuestionTest', '', GETDATE())",
connection))
{
connection.Open();
command.ExecuteNonQuery();
}
}
}
private void OnChange(object sender, SqlNotificationEventArgs e)
{
_notificationReceived = true;
}
private bool CanRequestNotifications()
{
try
{
var sqlClientPermission = new SqlClientPermission(PermissionState.Unrestricted);
sqlClientPermission.Demand();
return true;
}
catch
{
return false;
}
}
我发出了以下 SQL 查询,试图更好地了解正在发生的事情:
select * from sys.service_queues -- can see my EventsToPublishChangeMessages queue
select is_broker_enabled from sys.databases where database_id=db_id() -- returns 1
select * from sys.dm_qn_subscriptions -- returns nothing
select * from sys.transmission_queue -- returns nothing
有人知道我可能做错了什么吗?
错误消息说:
SqlDependency.Start()
must be called prior to execution of a command added to the SqlDependency instance
SqlDependency.Start
上的帮助指出:
Starts the listener for receiving dependency change notifications.
...
Ensure that Start is only called once per AppDomain
因此,在您的应用程序启动的某处放置一个 Start()
调用。由于这看起来像一个测试方法,可能在 [ClassInitialize]
装饰方法中。
我是第一次使用 Sql Server Service Broker,尝试在特定 table 发生变化时订阅通知。当我调用 command.ExecuteReader()
:
System.InvalidOperationException : When using SqlDependency without providing an options value, SqlDependency.Start() must be called prior to execution of a command added to the SqlDependency instance.
我已经创建了一个测试来重现该场景(为简洁起见省略了一些相关方法),如下所示:
private string _queueName = "EventsToPublishChangeMessages";
private bool _notificationReceived;
[Test]
public void WhyDoesExceptionIndcateSqlDependencyStartHasNotBeenCalledPriorToCommandExecuteReader()
{
Console.WriteLine($"canRequestNotifications: {CanRequestNotifications()}"); // returns true
var connectionString = GetConnectionString();
var started = SqlDependency.Start(connectionString, _queueName); // exception below seems to suggest that I haven't started the SqlDependency. Am I doing something wrong on this line?
Console.WriteLine($"Started:{started}"); // returns true
var connection = new SqlConnection(connectionString);
var command = new SqlCommand("SELECT Id, EventType, [Data], Created FROM dbo.EventsToPublish", connection);
var sqlDependency = new SqlDependency(command);
sqlDependency.OnChange += OnChange;
connection.Open();
// The following line causes the exception:
// System.InvalidOperationException : When using SqlDependency without providing an options value, SqlDependency.Start() must be called prior to execution of a command added to the SqlDependency instance.
// But you can see that I *did* call SqlDependency.Start() above.
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Process(reader);
}
}
TryToTriggerANotification(connectionString);
Thread.Sleep(5000); // ie. wait a few seconds to ensure notification-handling background thread has had a chance to complete.
Assert.That(_notificationReceived, Is.True);
}
private void TryToTriggerANotification(string connectionString)
{
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(
"INSERT INTO dbo.EventsToPublish(Id, EventType, [Data], Created) VALUES(newid(), 'WhosebugQuestionTest', '', GETDATE())",
connection))
{
connection.Open();
command.ExecuteNonQuery();
}
}
}
private void OnChange(object sender, SqlNotificationEventArgs e)
{
_notificationReceived = true;
}
private bool CanRequestNotifications()
{
try
{
var sqlClientPermission = new SqlClientPermission(PermissionState.Unrestricted);
sqlClientPermission.Demand();
return true;
}
catch
{
return false;
}
}
我发出了以下 SQL 查询,试图更好地了解正在发生的事情:
select * from sys.service_queues -- can see my EventsToPublishChangeMessages queue
select is_broker_enabled from sys.databases where database_id=db_id() -- returns 1
select * from sys.dm_qn_subscriptions -- returns nothing
select * from sys.transmission_queue -- returns nothing
有人知道我可能做错了什么吗?
错误消息说:
SqlDependency.Start()
must be called prior to execution of a command added to the SqlDependency instance
SqlDependency.Start
上的帮助指出:
Starts the listener for receiving dependency change notifications.
...
Ensure that Start is only called once per AppDomain
因此,在您的应用程序启动的某处放置一个 Start()
调用。由于这看起来像一个测试方法,可能在 [ClassInitialize]
装饰方法中。