- Silverlight Best Practices: Asynchronous Programming
- Using Action for an Event Façade
- Using the Asynchronous Programming Model (APM)
- The Problem: Nested Service Calls
- Summary
Using the Asynchronous Programming Model (APM)
While the event-based code works well for the most part, there is a slight nuance that is important to understand when building Silverlight applications. By default, the code above will return on the User Interface (UI) thread. This is safer because most of the time the end result of calling a service is to display the result to the user.
This can introduce problems, however, when the service returns information that must be processed prior to displaying. If heavy computations are required or there is a large amount of data returning from the call, the result could block the UI thread and degrade Silverlight performance. In those cases, it is necessary to force the call to the Asynchronous Programming Model (APM). Instead of using the default event-based model that was generated by the Silverlight proxy, you cast the client proxy to the service interface to gain access to the APM methods. Here is the code to call the service using the APM:
readonly IMyServices _client = new MyServicesClient(); public void CallUsingAPM() { _client.BeginAdd(1, 2, _EndCallUsingAPM, null); } private void _EndCallUsingAPM(IAsyncResult ar) { var result = _client.EndAdd(ar); }
Notice that this method involves a slightly different approach. Instead of registering to an event, you pass a delegate to the begin method. The delegate receives the result but must call the end method to resolve the final result. It is important to note that instead of using the proxy directly, the client was cast to the published service interface to expose the APM methods that start with Add and End. You can also use the ChannelFactory class to create the appropriate channel. To learn more about this approach, read about abstracting WCF service calls.
One advantage of wrapping the method in an Action façade is that you can also use the exact same signature to process the call using the APM model. The signature introduced earlier for events can be reused with the APM model, like this:
readonly IMyServices _client = new MyServicesClient(); public void Add(int x, int y, ResultFromAdd result) { _client.BeginAdd(1, 2, _EndCallUsingAPM, result); } private void _EndCallUsingAPM(IAsyncResult ar) { var result = (ResultFromAdd) ar.AsyncState; try { result(null, _client.EndAdd(ar)); } catch(Exception ex) { result(ex, 0); } }
Note that the service will be called exactly the same way whether you use the event approach or the APM model. You can see the APM model requires a little extra care to appropriately process exceptions, but the advantage is that you can execute and process the result on a background thread. Once processing is complete, you can return the results to the UI thread using the dispatcher.