2017-07-30 43 views
0

我第一次使用Sql Server Service Broker,试图在特定表中发生更改时订阅通知。我发现了以下情况例外,当我打电话command.ExecuteReader()使用SqlDependency无法接收更改通知

System.InvalidOperationException:当使用的SqlDependency没有提供期权价值,SqlDependency.Start()之前,必须执行命令叫加入的SqlDependency实例。

我创建了一个测试,以重现的场景(留出一些对简洁的相关方法),具体如下:

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(), 'StackOverflowQuestionTest', '', 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 

有没有人有任何想法,我可能做错了什么?

+0

是否https://stackoverflow.com/questions/19155819/error-when-using-sqldependency-without-providing-an-options-value help?或者https://stackoverflow.com/questions/27411100/sqldependency-startconnectionstring-return-every-time-false? – mjwills

+0

@mjwills第二个链接的答案有助于解决问题,即。使用带选项参数的SqlDependency构造函数。如果您想发布回复而不是评论,我很乐意将其标记为答案。谢谢! –

回答

0

错误消息说:

SqlDependency.Start()之前,必须执行命令叫加入的SqlDependency实例

上的帮助SqlDependency.Start状态:

启动监听器接收依赖性更改通知。

...

确保启动时才调用每个AppDomain

所以放置Start()呼叫某处您的应用程序的启动。由于这看起来像一种测试方法,可能在[ClassInitialize]装饰方法中。

+0

还有一点比这个还要多,但绝对问题的一部分是我每次调用SqlDependency.Start()时每个AppDomain不止一次 - 在我最初的情况下,我有两个测试调用了同一个测试系统包含SqlDependency.Start()调用。 对于上面编写的示例测试,我还必须使用带有选项和超时的构造函数重载,并指定“SERVICE = [my-service-name]”选项来解决此问题。 –

+0

@remus小问题:SqlDependency.Start()在AppDomain启动时调用它们。在SqlDependency.Start()之前调用SqlDependency.Stop()是否有意义? – Natalya

+0

@Natalya请问作为一个单独的问题 –