SqlDependency订阅在关机时不会从dm_qn_subscriptions中删除



我的SqlDependency工作正常,当应用程序退出时,代理队列和服务被正确删除(我在终止进程之前按照建议执行SqlDependency. stop(…)),但是我注意到由SqlDependency创建的通知订阅仍然存在于表"sys "中。Dm_qn_subscriptions "在应用程序关闭后。

如果我稍后(应用程序关闭后)执行应该使此订阅触发的条件,它似乎确实触发了,因为SQL Server在事件查看器中记录了一条信息消息,其效果为:

会话句柄的查询通知对话框'{3F03B693-C0A5-E211-A97B-E06995EBDB20}.'关闭的原因如下错误:'<?xml version="1.0"?><Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error"><Code>-8490</Code><Description>Cannot find the remote service &apos;SqlQueryNotificationService-0ea1f686-e554-4e25-aa7d-4f6d85171cc3&apos; because it does not exist.</Description></Error>' .

,然后从"sys.dm_qn_subscriptions"中删除订阅。

注意:当应用程序处于活动状态时,订阅也会正确触发。就我的应用程序而言,一切正常,但让我担心的是,一旦它们所依赖的代理队列/服务终止,订阅不会在数据库系统表中自动清除。这可能导致(至少)数据库中积累了大量的phantom/undead订阅记录,并在事件查看器中产生不必要的SQL Server清理消息(每个应用程序运行都会在"sys.dm_qn_subscriptions"中生成新的undead记录)。

此行为是否正常?事情能变得更整洁吗?

这是正常行为。QN是长期存在的,它们将在数据库重启时触发(因此也将在服务器重启后触发)。但是SqlDependency设置了一个临时服务/队列来接收通知,这些应该在使用对话计时器和内部激活崩溃的情况下被拆除。这两种机制的交互方式就是您所看到的ERRORLOG污染。没有什么不好的发生,至少不经常发生,但显然不整洁。

事情可以变得更整洁吗?

你可以直接使用SqlNotificationRequest来滚动你自己的解决方案,它不再提供创建服务/队列来接收你的appdomain通知并将它们路由到适当的SqlDependency.OnChange事件的"服务"。根据具体情况,也有可行的替代方案。但这是相当低层次的工作,你可能最终以比原来的SqlDependency解决方案更糟糕的方式解决问题…

顺便说一句,没有办法在应用程序退出时"删除"挂起的QN订阅。这个问题是QN用作通知传递机制的单向对话框所固有的。适当的通知(订阅)应该由订阅者发起,并且通知应该是从目标(通知者)返回到发起者(订阅者)的响应消息。

如果你不介意有点放肆的话,我已经找到了一种方法来清理这些出口…

首先,设置一个onDependencyChange可以观察到的标志,让它知道重新订阅查询。

第二,设置标志并执行一个什么都不做的更新,你知道这会触发依赖订阅。

 update foo_master set foo_bar = foo_bar where foo_id = @id;

我的依赖监视是在单独的行上完成的,所以我只需要激活一行就可以启动它。这可能不是您希望在大型结果集上执行的操作。

在我的FormClosing事件中,我在断开连接之前触发每个依赖项。

部分代码:

Private _dependency As SqlDependency = Nothing
Private _beingKilled = False
' dependency is set up in loadRecord(ByVal idRow as Integer)
Private Sub onDependencyChange(ByVal sender As Object, ByVal e As SqlNotificationEventArgs)
    ' This event may occur on a thread pool thread; It is illegal to update the UI from a worker thread.
    ' The following code checks to see if it is safe update the UI.
    Dim iSync As ISynchronizeInvoke = CType(_connection.masterForm, ISynchronizeInvoke)
    ' If InvokeRequired returns True, the code is executing on a worker thread.
    If iSync.InvokeRequired Then
        Dim tempDelegate As New OnChangeEventHandler(AddressOf onDependencyChange) ' Create a delegate to perform the thread switch
        Dim args() As Object = {sender, e}
        iSync.BeginInvoke(tempDelegate, args) ' Marshal the data from the worker thread to the UI thread.
    Else
        RemoveHandler _dependency.OnChange, AddressOf onDependencyChange
        If Not _beingKilled Then loadRecord(_id)
    End If
End Sub

然后简单地将_beingKilled设置为True并执行不做任何更新

相关内容

  • 没有找到相关文章

最新更新