Callbacks

Callbacks are one of the preferred ways to handle asynchronous calls because they simplify your code and eliminate the overhead of having to continuously poll a request that is in progress to see whether it has completed. The second-to-last parameter of the BeginInvoke method (or BeginMethodName in an XML Web service proxy class) accepts a reference to the method that you want triggered when the method call is complete.

To use a callback successfully, your callback method must have the same signature as the System.AsyncCallback delegate. That means it needs to accept a single IAsyncResult parameter. Here's an example:

 Public Sub AsyncCallback(ByVal ar As IAsyncResult)     ' (This code will be triggered when the method call is complete.) End Sub 

The callback code will then use the IAsyncResult object to call the appropriate EndInvoke method (or EndMethodName in a Web service proxy class) and retrieve the result.

Consider, for example, the CustomerDB.GetCustomers method introduced in Chapter 3, which returns a DataSet of customer information. You can call this method asynchronously with a callback by creating a delegate and using BeginInvoke. We'll walk through this process step by step.

First, you define the delegate. Notice that this delegate can be used generically with any method that requires no parameters and returns a DataSet.

 Delegate Function GetDataSet() As DataSet 

In addition, you need to create several form-level variables because the delegate variable must be accessible in more than one method (both the calling method and the callback).

 Private DB As New CustomerDB() Private GetData As New GetDataSet(AddressOf DB.GetCustomers) 

Next, use the delegate to call the method:

 GetData.BeginInvoke(AddressOf AsyncCallback, Nothing) 

When the DataSet is returned from the remote component, the callback will be triggered:

 Public Sub AsyncCallback(ByVal ar As IAsyncResult)     Dim ds As DataSet = GetData.EndInvoke(ar)     ' (Process the DataSet here.) End Sub 

Note

Although it's not strictly necessary, it's always good design practice to call EndInvoke even if you don't need to retrieve a return value. One reason is that your method call might have met with an error. Your code won't be notified of this exception until it attempts to call the EndInvoke method, at which point the exception will be rethrown.


Using callbacks seems fairly straightforward, but it actually involves several of the finer points of thread management. Here are some considerations:

  • Callbacks don't indicate the method or delegate that triggered them. Therefore, if you are executing several asynchronous calls at the same time, you will either need to direct these to separate callback methods or use the IAsyncResult.AsyncState object.

  • Callbacks are typically not executed on the same thread as the calling code. That means that they can execute at the same time as other code in your application, which opens your code up to a whole host of synchronization problems.

  • When calling a remote component or XML Web service asynchronously, you are actually calling the proxy class asynchronously. Therefore, in a .NET Remoting scenario, you won't have to worry about using a bidirectional channel and listening for messages from the server because the server isn't triggering your callback. If this is at all hazy, refer back to Figure 6-4.

The first problem is the easiest to resolve. You just create an object that tracks the necessary information and submit it with the request. This object is automatically assigned to the IAsyncResult.AsyncState property and provided to the callback. Typically, this is a custom object that includes a reference to the delegate or proxy class that started the method invocation. For example, you can define the following state object for the stock quote XML Web service (as shown in Listing 6-7).

Listing 6-7 A state object for the stock quote service
 Public Class StockState     Public Proxy As StockQuoteService     Public Ticker As String     Public Sub New(proxy As StockQuoteService, ticker As String)         Me.Proxy = proxy         Me.Ticker = ticker     End Sub End Class 

Before calling the method, you create an instance of the state object. You then submit the state object with the asynchronous call:

 Dim Proxy As New StockQuoteService() Dim State As New StockState(Proxy, "MSFT") Proxy.BeginGetStockQuote("MSFT", AddressOf StockCallback, State) 

The callback method can then retrieve all the information it needs from the state object. Remember, the StockCallback is providing the "answer" to your original asynchronous call. However, the answer isn't of much use if you don't remember what the original question was. The StockState object stores that information, including the proxy class you should use and the ticker value that was submitted, as shown in Listing 6-8. In this example, the stock information is written to a console window.

Listing 6-8 A callback that uses a state object
 Public Sub StockCallback(ByVal ar As IAsyncResult)     ' You must cast the AsyncState object to the correct type.     Dim State As StockState = CType(ar.AsyncState, StockState)     ' Retrieve the result.     Dim Result As Decimal = StockState.Proxy.EndGetStockQuote(ar)     ' Display the combined information.     Dim Message As String     Message = "Ticker: " & State.Ticker & " is at " & Result.ToString()     Console.WriteLine(Message) End Sub 

The second problem the fact that the callback executes on a separate thread is more difficult to resolve and can lead to subtle, infuriating errors if it is not handled correctly. There is no easy way to pass information to another thread. You might think that you could solve the problem by raising a custom event. Unfortunately, this won't help at all because the event handler will execute on the same thread as the callback. Similarly, you might believe that you can avoid the problem by writing the retrieved information directly to the user interface. However, modifying the user interface from a thread other than the one that owns it is similarly problematic and is likely to lead to subtle, maddening problems.

Ultimately, if more than one thread needs to interact, you need to add some level of synchronization. We'll consider your options a little later in this chapter.



Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net