2

我有一个带有显示作业列表的 DataGridView 的 VB.net 应用程序,从 SQL 查询中检索到 DataTable,然后是 DataView,最后是 DataGridView.DataSource 的 BindingSource。我最近在设置中添加了 SqlNotificationRequest 功能,因此当用户 B 对列表中的作业执行操作时,用户 A 会立即更新。

我使用这篇 MSDN 文章 ( http://msdn.microsoft.com/en-US/library/3ht3391b(v=vs.80).aspx ) 作为我开发的基础,它工作正常。当用户想要更改显示作业的 SQL 查询的参数(例如显示的日期)时,就会出现问题。目前,我正在创建一个带有新通知的新 SqlCommand,但是在用户更改了几次日期(比如 30 次)但没有数据更改之后,当发生通知超时时,当回调处理程序尝试 EndExecuteReader 时,我收到上述错误。我的回调处理程序如下:

    Private Sub OnSalesReaderComplete(ByVal asynResult As IAsyncResult)
    ' You may not interact with the form and its contents
    ' from a different thread, and this callback procedure
    ' is all but guaranteed to be running from a different thread
    ' than the form. Therefore you cannot simply call code that 
    ' updates the UI.
    ' Instead, you must call the procedure from the form's thread.
    ' This code will use recursion to switch from the thread pool
    ' to the UI thread.
    If Me.InvokeRequired Then
        myStatus.addHistory("OnSalesReaderComplete - Background", "Sub")
        Dim switchThreads As New AsyncCallback(AddressOf Me.OnSalesReaderComplete)
        Dim args() As Object = {asynResult}
        Me.BeginInvoke(switchThreads, args)
        Exit Sub
    End If

    ' At this point, this code will run on the UI thread.
    Try
        myStatus.addHistory("OnSalesReaderComplete - UI", "Sub")
        Dim sourceText, rSalesId As String

        waitInProgressSales = False
        Trace.WriteLine(String.Format("Sales:asynResult.IsCompleted1: {0}", asynResult.IsCompleted.ToString), "SqlNotificationRequest")
        Trace.WriteLine(String.Format("Sales:asynResult.CompletedSynchronously: {0}", asynResult.CompletedSynchronously.ToString), "SqlNotificationRequest")
        Dim reader As SqlDataReader = DirectCast(asynResult.AsyncState, SqlCommand).EndExecuteReader(asynResult)
        Trace.WriteLine(String.Format("Sales:asynResult.IsCompleted2: {0}", asynResult.IsCompleted.ToString), "SqlNotificationRequest")
        Do While reader.Read
            ' Empty queue of messages.
            ' Application logic could parse
            ' the queue data to determine why things.
            'For i As Integer = 0 To reader.FieldCount - 1
            '    'Debug.WriteLine(reader(i).ToString())
            '    Console.WriteLine(reader(i).ToString)
            'Next

            Dim bytesQN As SqlBytes = reader.GetSqlBytes(reader.GetOrdinal("message_body"))
            Dim rdrXml As XmlReader = New XmlTextReader(bytesQN.Stream)
            Do While rdrXml.Read
                Select Case rdrXml.NodeType
                    Case XmlNodeType.Element
                        Select Case rdrXml.LocalName
                            Case "QueryNotification"
                                sourceText = rdrXml.GetAttribute("source")
                            Case "Message"
                                rSalesId = rdrXml.ReadElementContentAsString
                        End Select
                End Select
            Loop
        Loop

        reader.Close()


        ' The user can decide to request
        ' a new notification by
        ' checking the check box on the form.
        ' However, if the user has requested to 
        ' exit, we need to do that instead.
        If exitRequestedSales Then
            'Me.Close()
            commandSales.Notification = Nothing
        Else
            Select Case sourceText.ToLower
                Case "data"
                    Trace.WriteLine(String.Format("SalesId: {0}, data notification", rSalesId), "SqlNotificationRequest")
                    Call GetSalesData(True, action.REATTACH)
                Case "timeout"
                    'check timeout is for this user and relates to current wait thread
                    Select Case salesId = rSalesId
                        Case True
                            Trace.WriteLine(String.Format("SalesId: {0}, timeout - current", rSalesId), "SqlNotificationRequest")
                            Call GetSalesData(True, False)
                        Case False
                            Trace.WriteLine(String.Format("SalesId: {0}, timeout - old", rSalesId), "SqlNotificationRequest")
                            Me.ListenSales()
                    End Select
            End Select
        End If
    Catch ex As Exception
        Call errorHandling(ex, "OnSalesReaderComplete", "Sub")
    End Try
End Sub

问题似乎在于,当通知是由于更新(“数据”)或超时是针对当前请求时,我只刷新数据并更新通知请求(使用 GetSalesData)。在其他通知上,应用程序调用 Me.ListenSales 创建 SQL 命令“WAITFOR (RECEIVE * FROM [QueueMessage])”并按照 MSDN 文章启动回调侦听器。如果我删除 Me.ListenSales 行,一旦收到不是由于数据或当前查询超时的通知,应用程序就会停止侦听通知。

我还在 MSDN 论坛(http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataproviders/thread/0f7a636d-0c9b-4b39-b341-6becf13873dc)上发布了相同的问题,因为我尝试了其他没有成功所以需要一些关于这是否可能以及如果可能如何如何的建议。

4

0 回答 0