我有一个带有显示作业列表的 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)上发布了相同的问题,因为我尝试了其他没有成功所以需要一些关于这是否可能以及如果可能如何如何的建议。