SQLクエリからDataTable、次にDataView、最後にDataGridView.DataSourceのBindingSourceに取得されたジョブのリストを表示するDataGridViewを備えたVB.netアプリがあります。最近、セットアップに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
問題は、通知が更新( "data")によるものであるか、タイムアウトが現在の要求に対するものである場合に、データを更新して通知要求を更新する(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 )にも投稿しました。成功するには、これが可能かどうか、可能であればどのように行うかについてのアドバイスが必要です。