Monday, March 21, 2016

Brownfield Async: Converting IAsyncResult to Task

We don't always get to start with new code; much of the time we're working with existing code and applications. Sometimes the code uses the Asynchronous Programming Model (APM) that relies on an IAsyncResult to deal with asynchronous methods. This methodology has always been a bit tricky to follow. Fortunately, we can convert those methods to the Task Asynchronous Pattern (TAP) by using a method that's included in Task (or more specifically the Task Factory).

Once we have a Task, we can take full advantage of the flexibility that Task affords us, including continuations, exception handling, and more.

For more information on using Task, check out the series of articles and videos available here: I'll Get Back to You: Task, Await, and Asynchronous Methods.

Let's take a look at some code that I developed while working on an actual application.

The code for this project is taken from the samples for "Clean Code: Homicidal Maniacs Read Code, Too!" available on GitHub: jeremybytes/clean-code-refactoring.

The Scenario: Asynchronous SOAP Proxy
I ran across this scenario while working as a contractor on a project. I worked on a client application that gets data from a series of SOAP services. The service proxies had already been generated for me, so I didn't have any control over them. Let's take a look at those proxy methods (well, not the same methods, but methods created the same way).

First, here's the service that we'll be using (PersonService.cs):


This is a pretty standard repository service. We're specifically looking at the "GetPeople" method that returns a list of Person objects.

To generate a proxy for a SOAP service, we right-click on our project references and select "Add Service Reference". This gives us the following dialog:


If we click on the "Advanced" button, we get the option to include asynchronous methods in our proxy class:


Notice that the radio button for "Generate asynchronous operations" is checked. This will generate APM methods that use IAsyncResult. (And notice, the option to "Generate task-based operations" is grayed-out. We won't go into the details of why this may or may not be available.)

For the application I was working with, the proxies had been generated with the APM methods. This gives us a pair of methods for each of our service methods.

Here are the "GetPeople" methods from the generated proxy class:


We have a pair of methods: "BeginGetPeople" and "EndGetPeople". These get tied together by passing the "IAsyncResult" return value from the "Begin" method as a parameter to the "End" method.

Unfortunately, using these methods directly is a bit complex. You can get an idea of it by looking at an example on MSDN. And that's one reason why we've mostly moved away from this pattern.

But again, sometimes we're stuck with code that has these methods, and we need to find a way to make use of them.

The Solution: Converting APM Methods to Task
Fortunately for us, there's a way to convert these difficult-to-use methods to a Task. Here's the code for that (from CatalogViewModel.cs):


For this, we call the "BeginGetPeople" method like we normally would. This gives us an "IAsyncResult" as a return value.

Then we use the "Task.Factory.FromAsync" method to convert this to a task. There are a number of overloads for this method. In this case, the first parameter is the "IAsyncResult" from our "Begin" method, and the second parameter is our "End" method.

Once we have this task, we can use it like any other Task. This includes adding a continuation when the asynchronous process completes:


Our Task returns a "List<Person>", and we can access this through the "Result" property just like we normally would. From there, we can perform whatever other operations we need.

Notice that we have this continuation marked as "NotOnFaulted", so this is our success path. There is a separate continuation to handle a faulted task (which means an exception was thrown during the asynchronous process).

As a side note, we're not using "await" here because this code was written in .NET 4.0 (before "await" was added). The code could not be upgraded to a newer version of .NET because the application needed to run on Windows XP machines (which only support up to .NET 4.0).

The entire method that uses this code is not very complex:


This is much easier than dealing with the APM methods directly (here's another MSDN example to show what's involved in that).

Wrap Up
Many times we're dealing with existing code that we don't have full control over. And that means we may have code which uses the Asynchronous Programming Model (APM) that passes around an IAsyncResult.

But rather than dealing with those methods directly, we can wrap them in a Task. This gives us access to the full power of using Task -- including continuations, exception handling, and much more.

And if we find that we have existing code that uses the APM methods (which can be difficult to debug and test), we can look at refactoring them to use Task instead. This also opens up the possibility of using the "await" operator to make our code even easier to read.

Just because we're working with existing code does not mean that we can't use more modern technologies. In the case of Task, we have a fairly easy way to update our APM methods to use Task instead.

Happy Coding!

2 comments:

  1. You can use the "Microsoft Async" nuget (https://www.nuget.org/packages/Microsoft.Bcl.Async/) package which back-ports async/await to 4.0 framework with KB2468871 clients

    ReplyDelete