SqlDependency subscription not dropped from dm_qn_subscriptions on shutdown

前端 未结 2 987
别那么骄傲
别那么骄傲 2021-02-10 14:47

My SqlDependency works fine, and the Broker Queue and Service get dropped properly when the application exits (I do execute SqlDependency.Stop(...) as recommen

相关标签:
2条回答
  • 2021-02-10 15:22

    This is the normal behavior. QN are long lived and they will fire upon a database restart (thus also will fire after a Server restart). But SqlDependency sets up a temporary service/queue to receive the notifications and these are supposed to be tear down in case of crash by using a dialog timer and internal activation. The way these two mechanisms interact is what you see, the ERRORLOG pollution. Nothing bad happens, at least not usually, but is obviously not neat.

    Can things be made neater?

    You can roll your own solution using directly SqlNotificationRequest which no longer provides the 'services' of creating a service/queue to receive your appdomain notifications and route them to the appropriate SqlDependency.OnChange event. There are viable alternatives, depending on the exact scenario. But is fairly low level work and you may end up solving the problems in a worse manner than the original SqlDependency solution...

    BTW there is no way to 'drop' the pending QN subscription on application exit. The problem is inherent in the one-way dialogs used as notification delivery mechanism by QN. Proper notifications (subscriptions) should be initiated by the subscriber and the notification should be a response message from target (notifier) back to initiator (subscriber).

    0 讨论(0)
  • 2021-02-10 15:24

    If you don't mind being a little cheezy, I've found a way to clean these up on exit...

    First, set a flag that the onDependencyChange can observe to let it know to not re-subscribe to the query.

    Second, set the flag and execute a do-nothing update that you know will trigger the dependency subscription.

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

    My dependency monitoring is done on individual rows, so I only have to tickle one row to get it to fire. It might not be something you'd want to do on a large result set.

    On my FormClosing event, I fire each of the dependencies before disconnecting.

    Partial code:

    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
    

    Then simply set _beingKilled to True and execute the do-nothing update.

    0 讨论(0)
提交回复
热议问题