Avoid This Web Services Gotcha
This simple Windows Forms code makes your asynchronous callbacks work the right way.
by Chris Kinsman
VSLive! SF, Day 2, February 13, 2002 When they first use Web Services, many developers begin to think of innovative ways to use them in an asynchronous fashion. There is one big "gotcha," however, that most developers will hit the first time they try this. When using with Web Services, you have to realize that when code is executing in your callback, you are likely to be on a different thread than your user interface code.
The Windows Forms classes are all single-threaded. They expect to be accessed by the same thread that created them. A control has a couple of thread-safe methods and properties, which you can use to determine whether you need a special technique to access the control from the current thread. If the InvokeRequired property returns false, it is safe to call any methods or properties on the control. If it returns true, you must use the Invoke() method of the control to call a delegate on the control's thread. This listing shows a simple Windows Forms application that calls a Web Service that returns order data from Nwind. The service is called asynchronously and a callback is used to populate the grid. Note the use of Invoke Required and Invoke to jump thread contexts:
Public Delegate Sub SetData(ByVal ar As _
IAsyncResult)
Private Sub Form1_Load(ByVal sender As _
System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
Dim oOrders As New localhost.Orders()
' Create the callback to pass to the
' asynchronous invocation
Dim wscb As New AsyncCallback( _
AddressOf WebServiceCallback)
' Call the web method asynchronously
' passing in the callback and the
' service itself
oOrders.BeginGetAllOrders(wscb, oOrders)
End Sub
Public Sub SetDataInGrid(ByVal ar _
As IAsyncResult)
Dim oOrders As localhost.Orders
' Grab the web service out of the async
' result object AsyncState property
oOrders = ar.AsyncState
' Get the data out of the finished web
' service
Dim ds As DataSet = _
oOrders.EndGetAllOrders(ar)
' Put the data into the grid
DataGrid1.DataMember = "Orders"
DataGrid1.DataSource = ds
End Sub
Public Sub WebServiceCallback(ByVal ar As _
IAsyncResult)
' When this callback executes we are on a
' different thread than the grid
' Windows Forms is single threaded so we
' need to call invoke to cross threads
Dim dlg As New SetData(AddressOf _
SetDataInGrid)
DataGrid1.Invoke(dlg, New Object() {ar})
End Sub
About the Author
Chris Kinsman is an instructor with Deep Training. He teaches courses on ASP.NET in both VB.NET and C#. In a previous life Chris was the CTO for a large scale Web site. Chris is a contributing editor for Visual Studio Magazine and is currently working on an ASP.NET book for SAMS. Chris is not a full-time speaker; most of his time is spent doing consulting with Vergent Software. He can be reached at: ckinsman@vergentsoftware.com.
Back to top
|