Simplifying Async Networking with Tasks in Silverlight 5

This is a modified excerpt from chapter 17 "Networking Basics" in Silverlight 5 in Action. You can thank James Manning (no relation to the publisher) for this being in the book. Persistence pays off :)

Shameless pimping: If you're looking for a one-stop shop for learning Silverlight 5, consider my book Silverlight 5 in Action, to be released in print near the end of this year. It's currently in MEAP, with purchasers able to download chapters as I make them available. Please, support my family, my children and buy my book … ok, I'll be honest: Please, support my electronics, robotics, CNC, woodworking, synthesis and other habits :)

For background, here's The HttpWebRequest/HttpWebResponse listing 17.2 referred to from the Tasks portion of the chapter

public class RequestState

{

public WebRequest Request { get; set; }

}



public partial class MainPage : UserControl

{

public MainPage()

{

InitializeComponent();

InitiateWebRequest();

}



private void InitiateWebRequest()

{

string uri = "http://localhost:47498/helloWorld.xml";



var request = HttpWebRequest.Create(uri);



var state = new RequestState() { Request = request };



var asyncResult =

(IAsyncResult)request.BeginGetResponse(

new AsyncCallback(ResponseCallback), state);

}



private static void ResponseCallback(IAsyncResult asynchronousResult)

{

var state = (RequestState)asynchronousResult.AsyncState;

var request = state.Request;



var response =

(HttpWebResponse)request.EndGetResponse(asynchronousResult);



var stream = response.GetResponseStream();

var reader = new StreamReader(stream);



string xmlFileText = reader.ReadToEnd();



Debug.WriteLine("Status Code: " +

response.StatusCode);

Debug.WriteLine("Status Description: " +

response.StatusDescription);

Debug.WriteLine(xmlFileText);

}

}



17.1.1 Simplifying with Tasks

The Task Parallel Library (TPL) is a section of the full .NET 4 framework which includes the ability to manage asynchronous and parallel operations. The subset of what is included in Silverlight 5 specifically targets the asynchronous part of the TPL, leaving out the parallel bits for now.

The Task class exists in the System.Threading.Tasks namespace, and is included as part of the runtime (no additional references required. It's a core Silverlight feature. Like Rx, Tasks were not created specifically to deal with networking, but they are very useful in this space in Silverlight, especially when working with HttpWebRequest and HttpWebResponse using the async request/response pattern. The Task class is where all the goodness lies.

Taking the same project we created in listing 17.1 and 17.2 on the request/response pattern, we can simplify the code using Tasks as shown in listing 17.10

Listing 17.10 Using Tasks to handle asynchronous operations

private void InitiateWebRequest()

{

string uri = "http://localhost:47498/helloWorld.xml";



var request = HttpWebRequest.Create(uri);



var webTask = Task.Factory.FromAsync<WebResponse>(

request.BeginGetResponse, request.EndGetResponse, null)

.ContinueWith(

task =>

{

var response = (HttpWebResponse)task.Result;

var stream = response.GetResponseStream();

var reader = new StreamReader(stream);

string xmlFileText = reader.ReadToEnd();



Debug.WriteLine("Method: " + response.Method);

Debug.WriteLine("Status Code: " + response.StatusCode);

Debug.WriteLine("Status Description: " + response.StatusDescription);

Debug.WriteLine(xmlFileText);

});

}

Go back and look at listing 17.2 and compare the code to what we have here. This is a significant reduction in code and number of functions when compared to the original listing. The function that makes that possible is the built-in Task.Factory.FromAsync method. That function has knowledge of the async request/response pattern, which just happens to be what is used by WebRequest and WebResponse when making your networking calls.

In this listing, I start by creating the request just as we did before. After that, however, I take a turn and instead of calling any other request functions ourselves, I create a task. The task, because it knows about the async pattern, needs only be provided with the begin and end functions and, optionally, a state object. Because we don't need to access the request ourselves, we're able to get rid of the state object we had in the previous version of this code.

When the task completes, the code in the .ContinueWith statement is executed. This is what you can use to chain networking calls, similar to what we did with Reactive Extensions, but arguably even cleaner. In this example, I simply get the response stream and display the XML file results to the debug window.

Just as was the case with Rx, this only scratches the surface of what you can do with Tasks, and with the TPL in general.

Now, all this may seem like a little much for a "basics" chapter, but it's important for you to understand that there are ways to manage this whole async thing, that don't involve losing all your hair and your sleep. I won't use Rx or Tasks in the examples in this book as they are layers between you and learning the fundamentals. However, in your real code, you'll almost certainly want to turn to them for non-trivial networking operations.

All of our examples so far have either used networking to a web project in the same solution, or have eschewed networking completely to simply simulate the asynchronous operations. Once you start accessing services on other sites, you'll run into cross-domain restrictions.

(the next section is on cross-domain networking and client access policy settings)

Other great references for this topic: