Hands-On Lab

Asynchronous Programming in the .NET Framework 4.5

Lab version:1.2.0

Last updated:2/6/2012

Contents

Overview

Exercise 1: The Async Method

Task 1 – Creating the Application in the Old Way

Task 2 – Using Asynchronous Code

Exercise 2: Concurrent Download

Task 1 – Adding Concurrent Download in a Separate Method

Task 2 – Verification

Exercise 3: Polling and Cancellation

Task 1 – Adding the Cancellation Feature

Task 2 – Polling the Links Status

Exercise 4: Async Library Refactoring

Task 1 – Creating an Asynchronous Class Library

Task 2 – Refactoring the Project

Appendix: Using Code Snippets

Overview

When your user interface is unresponsive or your server does not scale, chances are your code needs to be more asynchronous. Microsoft .NET Framework 4.5 introduces new language features in C# and Visual Basic to provide a new foundation for asynchrony in .NET programming. This new foundation makes asynchronous programming very similar to synchronous programming.

In this Hands-On Lab you will learn how to implement asynchronous programming with the new features introduced by Microsoft .NET Framework 4.5.

Introduction to Asynchronous Operations

Asynchronous operations are operations that are initiated and continue running concurrently with the invoking code.

When conforming to the .NET Task-Based Asynchronous Pattern (TAP), asynchronous methods return a task, which represents the ongoing operation and enables you towait for its eventual outcome.

Invoking and then waiting asynchronously for a task-returning method to complete has been simplified to a single-line instruction: “await myAsyncMethod();”. With this new asynchronous model, the control flow is completely unaltered. What is more, there are no callbacks because the compiler takes care of creating and signing them up.

Throughout this lab, you will become more familiar with the implementation of simple asynchronous logic to prevent blocking the UI when performing remote calls or intensive CPU processing.

Note: You can find more references on .NET Asynchronous programming and the Task-Based Asynchronous Pattern at

Objectives

In this hands-on lab, you will learn how to:

  • Convert synchronous code into asynchronous code
  • Perform tasks without blocking the UI
  • Run concurrent tasks
  • Implement cancellation and polling features
  • Generate a DLL library that exposes asynchronous methods

Prerequisites

  • Microsoft Visual Studio 11 Beta

Setup

Throughout the lab document, you will be instructed to insert code blocks. For your convenience, most of that code is provided as Visual Studio Code Snippets, which you can use from within Visual Studio to avoid having to add it manually.

To install the code snippets:

  1. Open a Windows Explorer window and browse to the lab’sSource\Setupfolder.
  2. Double-click theSetup.cmd file in this folder to install the Visual Studio Code Snippets.

If you are not familiar with the Visual Studio Code Snippets, and want to learn how to use them, you can refer to the appendix from this document ‘Using Code Snippets’.

Exercises

This hands-on lab includes the following exercises:

  1. The Async Method
  2. Concurrent Download
  3. Polling and Cancellation
  4. Async Library Refactoring

Note: Each exercise is accompanied by a starting solution—located in the Begin folder of the exercise—that allows you to follow each exercise independently of the others. Please be aware that the code snippets that are added during an exercise are missing from these starting solutions and that they will not necessarily work until you complete the exercise.

Inside the source code for an exercise, you will also find an End folder containing a Visual Studio solution with the code that results from completing the steps in the corresponding exercise. You can use these solutions as guidance if you need additional help as you work through this hands-on lab.

Exercise 1: The Async Method

In this exercise, you will create a very simple application that will download the HTML content of a web page and, using regular expressions, search through that content looking for any link. Then, you will display those links in a ListBox. You will build this application in two ways. First, you will build the application using synchronous code and examine the issues with this implementation. Second, you will modify that application to use the asynchronous code so you can see how this implementation addresses the issues with the first version of the application.

Task 1 – Creating the Application in the Old Way

In this task, you will create a WPF application to download a web page and find any link in the HTML. You will write the code in the old way by using synchronous calls, and finally, run the application to see the disadvantages of this way of programming.

  1. Open Visual Studio 11 and create a new WPF Application projectwith the name AsyncLab. Choose the language of your preference: C# or Visual Basic.
  2. In the MainWindow.xaml designer, resize the window to 640x480.
  3. Drop a TextBox on the left to display the HTML and a ListBox on the right to display the found links.
  4. Drop a button at the bottom and set its content to “Start”. You will get the following XAMLcode:

XAML

Window x:Class="MainWindow"

xmlns="

xmlns:x="

Title="MainWindow"

Width="640"Height="480"

Grid

TextBox Height="379"HorizontalAlignment="Left" Margin="12,12,0,0"

Name="textBox1"VerticalAlignment="Top" Width="294" />

ListBox Height="379"HorizontalAlignment="Right" Margin="0,12,12,0"

Name="listBox1"VerticalAlignment="Top" Width="294" />

Button Content="Start" Height="23"HorizontalAlignment="Left"

Margin="272,406,0,0"Name=“startButton” VerticalAlignment="Top"

Width="75" />

</Grid

</Window

Note: Make sure you only copy the highlighted lines.

  1. Double-click the Start button to create a Click event handler.
  2. Add code to disable the Start button while the work isin progress, andenable it once the work is completed.

(Code Snippet – Async Lab - Ex01 - Disable Enable Start - CS)

C#

privatevoidstartButton_Click(object sender, RoutedEventArgs e)

{

startButton.IsEnabled = false;

startButton.IsEnabled = true;

}

(Code Snippet – Async Lab - Ex01 - Disable Enable Start - VB)

Visual Basic

PrivateSubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesStart.Click

startButton.IsEnabled = False

startButton.IsEnabled = True

EndSub

  1. Define a list of strings to store the URIs at the beginning of the Start event.

(Code Snippet – Async Lab - Ex01 - URIs List - CS)

C#

privatevoidstartButton_Click(object sender, RoutedEventArgs e)

{

Liststringuris = newListstring>();

startButton.IsEnabled = false;

(Code Snippet – Async Lab - Ex01 - URIs List - VB)

Visual Basic

PrivateSubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesstartButton.Click

DimurisAsNewList(OfString)()

startButton.IsEnabled = False

  1. In between the code that disables and enables the Start button, insert a try-catch block for the main functionality of the application. Then, implement an exception handler to display a message box with the description of the error.

(Code Snippet – Async Lab - Ex01 - TryCatch - CS)

C#

Liststringuris = newListstring>();

startButton.IsEnabled = false;

try

{

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

startButton.IsEnabled = true;

(Code Snippet – Async Lab - Ex01 - TryCatch -VB)

Visual Basic

DimurisAsNewList(OfString)()

startButton.IsEnabled = False

Try

Catch ex AsException

MessageBox.Show(ex.ToString())

EndTry

startButton.IsEnabled = True

  1. Add aproject reference to System.Net.
  2. Import the following namespaces:

(Code Snippet – Async Lab - Ex01 - Namespaces - CS)

C#

using System.Net;

usingSystem.Text.RegularExpressions;

(Code Snippet – Async Lab - Ex01 - Namespaces - VB)

Visual Basic

Imports System.Net

ImportsSystem.Text.RegularExpressions

  1. Insert the following code to download the HTML content from a specific URL. Then, once it is downloaded, the result is assigned to the textboxvalue.

(Code Snippet – Async Lab - Ex01 - Download Page - CS)

C#

try

{

WebClient client = new WebClient();

string result = client.DownloadString("

textBox1.Text = result;

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

(Code Snippet – Async Lab - Ex01 - Download Page - VB)

Visual Basic

Try

Dim clientAsNewWebClient()

Dim result AsString=client.DownloadString("

textBox1.Text = result

Catch ex AsException

MessageBox.Show(ex.ToString())

EndTry

  1. Use a regular expression to search for links, store them in a URIs list, and then, bind the URIs list to the ListBox.

(Code Snippet – Async Lab - Ex01 - Search Links - CS)

C#

textBox1.Text = result;

MatchCollection mc = Regex.Matches(result,

"href\\s*=\\s*(?:\"(?<1> RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

uris.Add(m.Groups[1].Value);

}

listBox1.ItemsSource = uris;

(Code Snippet – Async Lab - Ex01 - Search Links - VB)

Visual Basic

textBox1.Text = result

Dim mc AsMatchCollection = Regex.Matches(result,

"href\s*=\s*(?:\""(?<1> RegexOptions.IgnoreCase)

ForEach m AsMatchIn mc

uris.Add(m.Groups(1).Value)

Next

listBox1.ItemsSource = uris

  1. Press F5 to start debugging. When the application starts, click the Start button.

Figure 1

Running the application

Note:Notice the window is unresponsive and the button is not being disabled because the UI is blocked while the application is downloading the web page.

Task 2 – Using Asynchronous Code

In this task, you will enhance the application you have started in the previous task to make it asynchronous. To do this, you will implement an asynchronous method to download the page in the background.

  1. Add a project reference to System.Net.Http.
  2. Make the following change at the top of the code file:

(Code Snippet – Async Lab - Ex01 - Namespaces - CS)

C#

usingSystem.Net.Http;

usingSystem.Text.RegularExpressions;

(Code Snippet – Async Lab - Ex01 - Namespaces - VB)

Visual Basic

ImportsSystem.Net.Http

ImportsSystem.Text.RegularExpressions

  1. Add the async keyword in the startButton_Click function.

C#

privateasync voidstartButton_Click(object sender, RoutedEventArgs e)

{

Visual Basic

PrivateAsync SubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesStart.Click

Note: The async keyword is one of the new keywords that .NET Framework 4.5 provides.The Async keyword communicatesto the compiler that the current method will contain asynchronous code.

  1. Replace the code that uses the WebClient class with a call to the GetAsync method of the HttpClient class.

C#

try

{

var response = newHttpClient().GetAsync("

string result = await response.Content.ReadAsStringAsync();

textBox1.Text = result;

...

Visual Basic

Try

Dim response = NewHttpClient().GetAsync("

Dim result AsString = response.Content.ReadAsStringAsync()

textBox1.Text = result

...

Note: The GetAsyncmethod returns a Task object. A Taskobject represents an asynchronous operation that may complete at some point in the future.

  1. Add the await keyword on the call to GetAsync().

(Code Snippet – Async Lab - Ex01 - Download Page Async - CS)

C#

try

{

var response = await newHttpClient().GetAsync("

string result = await response.Content.ReadAsStringAsync();

textBox1.Text = result;

...

(Code Snippet – Async Lab - Ex01 - Download Page Async - VB)

Visual Basic

Try

Dim response = AwaitNewHttpClient().GetAsync("

Dim result AsString = Await response.Content.ReadAsStringAsync()

textBox1.Text = result

...

Note:When you add the await keyword, you are telling the compiler to asynchronously wait for the task returned by the method. This means that the rest of the code will be executed as a callback after the awaited method completes. You do not need to change your try-catch block in order to make this work.The exceptions that happen in the background or in the foreground will stillbe caught.

  1. Press F5 to run the application and click the Start button. Notice that the application is responsive while the Start button is disabled. UI thread is never locked because the work is processed in the background.

Exercise 2: Concurrent Download

In this exercise, you will enhance the solution from Exercise 1 byenabling users to interact with the application while the contentsarebeing downloaded. You will learn how to use the Task object in order to prevent blocking the user interface (UI).

After completing the exercise, the application will remain responsive while it downloads the full web page.

Task 1 – AddingConcurrent Downloadin a Separate Method

  1. Open Visual Studio 11 and load AsyncLab-Ex2-Begin.slnsolution located inthe Source\[CS|VB]\Ex2-Concurrency\Begin folder of this lab. You can also continue working with the solution you’ve obtained after completing Exercise 1.
  2. Open MainWindow.xaml.csor MainWindow.xaml.vbcode-behind and import theSystem.Collection.ObjectModel namespace.

C#

usingSystem.Collections.ObjectModel;

Visual Basic

ImportsSystem.Collections.ObjectModel

  1. In startButton_Clickevent handler, replace the uris local variabletype with an ObservableCollection type.

C#

privateasyncvoidstartButton_Click(object sender, RoutedEventArgs e)

{

Liststringuris = newListstring>();

ObservableCollectionstringuris = newObservableCollectionstring>();

startButton.IsEnabled = false;

try

{

...

Visual Basic

PrivateAsyncSubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesStart.Click

DimurisAsNewList(OfString)()

DimurisAsNewObservableCollection(OfString)()

startButton.IsEnabled = False

  1. Send the URI processing to thebackground in order to improve the application responsiveness. Enclose the filter and list manipulation logic into a lambda expression, whichwill be executed in background threadwhen calling Task.Run(). This task will be completed asynchronously, as it includes the await keyword.

(Code Snippet – Async Lab - Ex02 - AwaitBlock - CS)

C#

privateasyncvoidstartButton_Click(object sender, RoutedEventArgs e)

{

...

try

{

...

textBox1.Text = result;

awaitTask.Run(() =>

{

MatchCollection mc = Regex.Matches(

result, "href\\s*=\\s*(?:\"(?<1>

RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

uris.Add(m.Groups[1].Value);

}

});

(Code Snippet – Async Lab - Ex02 - AwaitBlock - VB)

Visual Basic

PrivateAsyncSubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesStart.Click

...

Try

...

textBox1.Text = result

AwaitTask.Run(Sub()

Dim mc AsMatchCollection = Regex.Matches(result,

"href\s*=\s*(?:\""(?<1> RegexOptions.IgnoreCase)

ForEach m AsMatchIn mc

uris.Add(m.Groups(1).Value)

Next

EndSub)

Note:By using Task.Run, you are explicitly running your logic on a different thread from the thread pool.

  1. Create a new class in the root of AsyncLab project and name it LinkInfo. This class will contain information about the links of the page. Insert the following code in theLinkInfo class.

(Code Snippet – Async Lab - Ex02 - LinkInfo - CS)

C#

namespaceAsyncLab

{

publicclassLinkInfo

{

publicstring Title { get; set; }

publicstring Html { get; set; }

publicint Length { get; set; }

}

}

(Code Snippet – Async Lab - Ex02 - LinkInfo- VB)

Visual Basic

PublicClassLinkInfo

PublicProperty Title() AsString

PublicProperty Html() AsString

PublicProperty Length AsInteger

EndClass

  1. Create a new method to download a page in theMainWindow.xaml.csor MainWindow.csml.vbcode-behind class. Name this methodDownloadItemAsyncand declare it with the Async prefix. This methodwill return an empty string in case the HTTPGEToperation cannot be resolved.

(Code Snippet – Async Lab - Ex02 -DownloadItemAsync - CS)

C#

private static asyncTaskLinkInfoDownloadItemAsync(UriitemUri)

{

string item;

try

{

HttpClienthttpClient = newHttpClient();

httpClient.MaxResponseContentBufferSize = 1000000;

var response = awaithttpClient.GetAsync(itemUri);

item = await response.Content.ReadAsStringAsync();

}

catch

{

item = string.Empty;

}

LinkInfolinkInfo = newLinkInfo {

Length = item.Length, Title = GetTitle(item), Html = item };

returnlinkInfo;

}

(Code Snippet – Async Lab - Ex02 - DownloadItemAsync - VB)

Visual Basic

Private SharedAsyncFunctionDownloadItemAsync(

ByValitemUri AsUri) AsTask(OfLinkInfo)

Dim item AsString

Try

DimhttpClientAsNewHttpClient

httpClient.MaxResponseContentBufferSize = 1000000

Dim response = AwaithttpClient.GetAsync(itemUri)

item = Await response.Content.ReadAsStringAsync()

Catch

item = String.Empty

EndTry

DimlinkInfoAsLinkInfo = NewLinkInfo() With {

.Length = item.Length, .Html = item, .Title = GetTitle(item)}

ReturnlinkInfo

EndFunction

Note: Notice we are setting an arbitrary HTTP client buffer size, which is higher than the default value (~65Kb). This is because many web messages may exceed this value and generate an application error.

  1. Create a GetTitle method after theDownloadItemAsync methodto return the text located inside the title tag.

(Code Snippet –Async Lab - Ex02 - GetTitle - CS)

C#

private staticstringGetTitle(string html)

{

if (html.Length == 0)

{

return"Not Found";

}

Match m = Regex.Match(html,

@"(?<=<title.*>)([\s\S]*)(?=</title>)", RegexOptions.IgnoreCase);

returnm.Value;

}

(Code Snippet –Async Lab - Ex02 - GetTitle - VB)

Visual Basic

Private SharedFunctionGetTitle(ByVal html AsString) AsString

If (html.Length.Equals(0)) Then

Return"Not Found"

EndIf

Dim m AsMatch = Regex.Match(Html, "(?<=<title.*>)([\s\S]*)(?=</title>)",

RegexOptions.IgnoreCase)

Returnm.Value

EndFunction

  1. Modify thestartButton_Clickevent handler logicto use theDownloadItemAsyncmethodandpopulate theListBoxwith the retrieved items.

(Code Snippet – AsyncLab - Ex02 - DownloadCompleted - CS)

C#

privateasyncvoidstartButton_Click(object sender, RoutedEventArgs e)

{

...

try

{

...

awaitTask.Run(() =>

{

MatchCollection mc = Regex.Matches(result,

"href\\s*=\\s*(?:\"(?<1>

RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

uris.Add(m.Groups[1].Value);

}

});

listBox1.ItemsSource = awaitTask.WhenAll(

fromuriinurisselectDownloadItemAsync(newUri(uri)));

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

}

(Code Snippet – AsyncLab - Ex02 - DownloadCompleted - VB)

Visual Basic

PrivateAsyncSubstartButton_Click(sender AsSystem.Object,

e AsSystem.Windows.RoutedEventArgs) HandlesStart.Click

...

Try

...

AwaitTask.Run(Sub()

Dim mc AsMatchCollection = Regex.Matches(

result, "href\s*=\s*(?:\""(?<1>

RegexOptions.IgnoreCase)

ForEach m AsMatchIn mc

uris.Add(m.Groups(1).Value)

Next

EndSub)

listBox1.ItemsSource = AwaitTask.WhenAll( _

FromuriInuris _

SelectDownloadItemAsync(NewUri(uri)))

Catch ex AsException

MessageBox.Show(ex.ToString())

EndTry

...

EndSub

Note:Notice that:

-When using Task.WhenAll, a task completes when all of the constituent tasks have completed. In this example, the await keyword before Task.WhenAllis forcing it to asynchronously wait for all of the items to complete before continuing the execution of the next instruction.

- When using Task.WhenAny,the returned task completes when any of the tasks completes.

  1. Open MainWindow.xamlXAML view and insert the following code before the Grid to show the link title and the length of the linked page.

XAML

Window x:Class="MainWindow"

xmlns="

xmlns:x="

Title="MainWindow" Height="480" Width="640">

Window.Resources

DataTemplate x:Key="DataTemplateItem">

StackPanel Orientation="Horizontal">

TextBlock

TextBlock.Text

MultiBindingStringFormat=" {0} - {1} ">

Binding Path="Length"/>

Binding Path="Title"/>

</MultiBinding

</TextBlock.Text

</TextBlock

</StackPanel

</DataTemplate

</Window.Resources

Grid

...

  1. Modify the ListBox to use the data template from the previous step.

XAML

Grid

TextBox Height="379"HorizontalAlignment="Left" Margin="12,12,0,0"

Name="textBox1"VerticalAlignment="Top" Width="294" />

ListBox Height="379"HorizontalAlignment="Right" Margin="0,12,12,0"

Name="listBox1"VerticalAlignment="Top" Width="294"

ItemTemplate="{DynamicResourceDataTemplateItem}"/>

Button Content="Start" Height="23"HorizontalAlignment="Left"

Margin="272,406,0,0"Name=“startButton” VerticalAlignment="Top"

Width="75" Click="startButton_Click" />