41
Hands-On Lab Asynchronous Programming in the .NET Framework 4.5 Lab version: 1.1.0.0 Last updated: 1/2/2013

Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Hands-On Lab

Asynchronous Programming in the .NET Framework 4.5

Lab version: 1.1.0.0

Last updated: 1/2/2013

Page 2: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

CONTENTS

OVERVIEW ............................................................................................................................................. 3

EXERCISE 1: ASYNC METHODS .......................................................................................................... 5 Task 1 – Creating the Application in the Old Way ............................................................................. 5

Task 2 – Using Asynchronous Code ................................................................................................ 11

EXERCISE 2: CONCURRENT DOWNLOAD ........................................................................................ 13 Task 1 – Adding Concurrent Download in a Separate Method ........................................................ 13

Task 2 – Verification ....................................................................................................................... 20

EXERCISE 3: POLLING AND CANCELLATION ................................................................................... 21 Task 1 – Adding the Cancellation Feature ....................................................................................... 21

Task 2 – Polling the Linked Pages to Detect Changes ...................................................................... 27

EXERCISE 4: ASYNC LIBRARY REFACTORING ................................................................................ 34 Task 1 – Creating an Asynchronous Class Library ............................................................................ 34

Task 2 – Refactoring the Project ..................................................................................................... 35

APPENDIX: USING CODE SNIPPETS.................................................................................................. 40

To give feedback please write to [email protected].

Copyright © 2013 by Microsoft Corporation. All rights reserved.

Page 3: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Overview

If your user interface is unresponsive or your server does not scale, chances are you could improve

matters with asynchronous code. Microsoft .NET Framework 4.5 introduces new language features in C#

and Visual Basic to make asynchronous programming in .NET far simpler. This new foundation makes

asynchronous programming very similar to synchronous (i.e., ordinary) 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 represent potentially slow work, they but return almost immediately, enabling

the initiating thread to get on with other work while the operation continues to run concurrently.

The language-level support for asynchronous operation requires methods to implement a particular

pattern, called the .NET Task-Based Asynchronous Pattern (TAP). An asynchronous method must return

without waiting for the operation it starts to complete. That would be true for any asynchronous

pattern, but to conform to the TAP, a method must also return an object that represents the ongoing

operation and enables you to discover its eventual outcome. C# and VB require this object to offer

certain functionality, and although do not require it to be of any particular type, it is very common

choice to return a Task object.

Tasks have been available since .NET 4.0, but invoking and then waiting asynchronously for a task-

returning method to complete used to require potentially complex callback-based code. But with .NET

4.5, it has been simplified to a single expression: “await MyAsyncMethod()”. Before .NET 4.5, the

callbacks required to handle the asynchronous work efficiently could make code hard to read,

particularly if you needed to use loops or other flow control constructs. With .NET 4.5’s new

asynchronous model, you can manage control flow in exactly the same way as you would with ordinary

synchronous code, because you do not need to write explicit callbacks. The compiler takes care of

creating and registering these for you, so you can still enjoy all the performance benefits of

asynchronous code, but you no longer have to pay the price of complexity.

Throughout this lab, you will become familiar with the implementation of simple asynchronous logic to

prevent blocking the UI when performing remote calls or other slow operations.

Page 4: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Note: Asynchronous techniques are not limited to UI code—they are important for getting the most

out of server-side code. The techniques are the same in either case though, so you can use the

approach shown in this lab on servers too. You can find more references on .NET Asynchronous

programming and the Task-Based Asynchronous Pattern at http://msdn.microsoft.com/async.

Objectives

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

Convert synchronous code into asynchronous code

Perform slow tasks without blocking the UI

Run multiple asynchronous operations concurrently

Implement cancellation and polling features

Create a DLL library that exposes asynchronous methods

Prerequisites

Microsoft Visual Studio 2013

Windows PowerShell (for setup scripts – already installed on Windows 8, Windows Server 2012,

Windows 7 and Windows Server 2008 R2)

Code Snippets 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.

You can optionally install the provided code snippets:

1. Run Visual Studio as an Administrator.

2. Select Tools|Code Snippets Manager.

3. Select Visual C# from the Languages dropdown list.

4. Click Add.

5. Browse to the lab’s Source folder located on your desktop’s folder TechEd

2014\Asynchronous Programming in the .NET Framework.

6. Select the Async Lab Snippets folder.

7. Click Select Folder.

Formatted: Font: Bold

Page 5: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

8. Click OK to close the Code Snippets Manager.

Exercises

This hands-on lab includes the following exercises:

1. Async Methods

2. Concurrent Download

3. Polling and Cancellation

4. Async Library Refactoring

Note: The exercises form a sequence, where each builds on the previous one. However, if you want to

tackle them out of order, you can. Exercises 2-4 are accompanied by a starting solution that you can

use if you did not complete the previous exercise. You may also find these useful if you choose not to

follow the instructions precisely. These starter solutions are in the Begin folder for each exercise.

(There is no Begin folder for Exercise 1 because you will start by creating a fresh project.) Please be

aware that the code snippets that you will add during an exercise are missing from these starting

solutions and that they will not necessarily work until you complete the exercise.

Inside the folder for each 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: Async Methods

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. To begin with, you will build the application using synchronous code and

examine the issues with this implementation. Then, you will modify your code to work asynchronously,

addressing 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 links 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 2013 and create a new WPF Application project with the name AsyncLab.

Choose either C# or Visual Basic, according to your preference.

Page 6: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

2. In the MainWindow.xaml designer, resize your application’s window to 640x480.

3. Drop a TextBox on the left to display the HTML and name it “textBox1”; drop and a ListBox on

the right to display the found links and name it “listBox1”.

4. Drop a button at the bottom and set its content to “Start” and its name to “Start”. Your XAML

should now look something like this:

XAML

<Window x:Class="AsyncLab.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

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="Start" VerticalAlignment="Top" Width="75" />

</Grid>

</Window>

Note: If you decide to copy and paste the XAML from this document rather than creating it

interactively in Visual Studio’s XAML designer, make sure you only copy the highlighted lines.

5. Double-click the Start button to create a Click event handler.

6. Add code to disable the Start button while the work is in progress, and enable it once the work

is completed.

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

C#

private void Start_Click(object sender, RoutedEventArgs e)

{

this.Start.IsEnabled = false;

this.Start.IsEnabled = true;

}

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

Visual Basic

Private Sub Start_Click(sender As Object, e As RoutedEventArgs) Handles

Start.Click

Page 7: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Me.Start.IsEnabled = False

Me.Start.IsEnabled = True

End Sub

7. At the beginning of your event handler, declare an initialize a variable holding a list of strings to

store the URIs.

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

C#

private void Start_Click(object sender, RoutedEventArgs e)

{

List<string> uris = new List<string>();

this.Start.IsEnabled = false;

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

Visual Basic

Private Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

Dim uris As New List(Of String)()

Me.Start.IsEnabled = False

8. In between the code that disables and enables the Start button, insert a try-catch block. (This

will contain the main functionality of the application.) In the catch block, implement an

exception handler to display a message box with the description of any error that occurs.

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

C#

List<string> uris = new List<string>();

this.Start.IsEnabled = false;

try

{

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

this.Start.IsEnabled = true;

Page 8: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

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

Visual Basic

Dim uris As New List(Of String)()

Me.Start.IsEnabled = False

Try

Catch ex As Exception

MessageBox.Show(ex.ToString())

End Try

Me.Start.IsEnabled = True

9. Add a project reference to System.Net.Http. (If the Reference Manager dialog lists multiple

versions, choose 4.0.0.0.)

10. Import the following namespaces:

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

C#

using System.Net.Http;

using System.Text.RegularExpressions;

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

Visual Basic

Imports System.Net.Http

Imports System.Text.RegularExpressions

11. Insert the following code to download the HTML content from a specific URL, and display the

result in the textbox.

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

C#

try

{

var response = new HttpClient()

.GetAsync("http://msdn.microsoft.com").Result;

string result = response.EnsureSuccessStatusCode().Content

.ReadAsStringAsync().Result;

this.textBox1.Text = result;

}

Page 9: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

catch (Exception)

{

MessageBox.Show(ex.ToString());

}

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

Visual Basic

Try

Dim response = New HttpClient().GetAsync("http://msdn.microsoft.com").Result

Dim result As String =

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().Result

Me.textBox1.Text = result

Catch ex As Exception

MessageBox.Show(ex.ToString())

End Try

Note: The HttpClient class only support asynchronous operation—its GetAsync and

ReadAsStringAsync methods both return tasks, and there are no corresponding Get or

ReadAsString methods. However, this code uses these tasks’ Result property, which

effectively converts this into synchronous operation. (If you read the Result of an incomplete

task, it blocks your thread until the task completes, so that it can return the result.) This means

that although we are using asynchronous methods (because that’s all HttpClient offers) we’re

using them in a synchronous way.

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

list to the ListBox.

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

C#

this.textBox1.Text = result;

MatchCollection mc = Regex.Matches(result,

"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

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

}

this.listBox1.ItemsSource = uris;

}

Page 10: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

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

Visual Basic

Me.textBox1.Text = result

Dim mc As MatchCollection = Regex.Matches(result,

"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)

For Each m As Match In mc

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

Next

Me.listBox1.ItemsSource = uris

Catch ex As Exception

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

Figure 1

Running application

Note: Notice that for as long as it takes to download the content, the window is unresponsive.

Also, the button does not change its appearance to indicate that it is disabled. Both of these

problems are caused by the fact that this code blocks the thread while the application is

downloading the web page.

Page 11: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Task 2 – Using Asynchronous Code

In this task, you will enhance the application you wrote in the previous task to use asynchronous

techniques. To do this, you will implement an asynchronous method to download the page in the

background.

1. Add the async keyword in the Start_Click function.

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

Visual Basic

Private Async Sub Start_Click(sender As Object, e As RoutedEventArgs) Handles

Start.Click

Note: The async keyword (or Async in VB) is new in .NET Framework 4.5. It tells the compiler

that the current method will contain asynchronous code.

Of course, right now your method does not yet contain any asynchronous code, so you may

see a compiler warning at this point, telling you that the async keyword only makes sense in a

method that uses await. The next two steps will fix this.

2. Remove the use of the Result property after the HttpClient GetAsync method call, and also

after the ReadAsStringAync call on the following line.

C#

try

{

var response = new HttpClient().GetAsync("http://msdn.microsoft.com");

string result =

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

this.textBox1.Text = result;

...

Visual Basic

Try

Dim response = New HttpClient().GetAsync("http://msdn.microsoft.com")

Dim result As String =

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

Me.textBox1.Text = result

...

Page 12: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Note: As before, we’re using the GetAsync and ReadAsStringAsync methods, but we’re no

longer reading the Result property, so we’re now going to be using HttpClient as intended:

asynchronously. We are not quite there yet, though, so you will get an error if you attempt to

compile the code right now. The next step will fix that.

3. Add the await keyword in front of the expression that calls GetAsync(), and also in front of the

one that calls ReadAsStringAsync().

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

C#

try

{

var response = await new HttpClient().GetAsync("http://msdn.microsoft.com");

string result = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

this.textBox1.Text = result;

...

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

Visual Basic

Try

Dim response = Await New HttpClient().GetAsync("http://msdn.microsoft.com")

Dim result As String = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

Me.textBox1.Text = result

...

Note: When you add the await keyword, you are telling the compiler that the task must

complete before the method can continue. Although it looks like it will simply block the thread

while waiting for this to happen (just like when we read the Result property earlier), under the

covers the compiler arranges for the rest of the code to execute as a callback after the awaited

operation completes. (Your click handler method will return in the meantime.) You do not

need to change your try-catch block in order to make this work. The try/catch block will still

catch any exceptions that happen, whether they occur in asynchronous operations for which

your code awaits, or in normal non-asynchronous parts of the code. This requires the compiler

to generate relatively complex code under the covers, code that you would have had to have

written yourself with earlier versions of .NET.

Page 13: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

4. Press F5 to run the application and click the Start button. Notice that the button now changes

visibly to a disabled state for as long as the download takes to complete, and that the

application remains responsive while that happens. With these modifications, your application

never blocks the UI thread, because the download work now happens in the background; the

click handler allows the UI thread to return to its message loop while that work is in progress.

Exercise 2: Concurrent Download

In this exercise, you will enhance the solution from Exercise 1 to download the content for all of the links

it discovered, while remaining responsive to user input.

Task 1 – Adding Concurrent Download in a Separate Method

1. Open Visual Studio 2013 and load AsyncLab-Ex2-Begin.sln solution located on your desktop in

the folder STechEd 2014\Asynchronous Programming in the .NET

Framework\Source\[CS|VB]\Ex2-Concurrency\Begin folder of this lab. Alternatively, if you

completed Exercise 1, you can continue working with the solution you created in that part.

2. Open MainWindow.xaml.cs or MainWindow.xaml.vb code-behind and import the

System.Collection.ObjectModel namespace. Click on the Ok button on the security warning.

C#

...

using System.Collections.ObjectModel;

Visual Basic

...

Imports System.Collections.ObjectModel

3. In Start_Click event handler, change the uris local variable to use the generic

ObservableCollection type instead the generic List type.

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

List<string> uris = new List<string>();

ObservableCollection<string> uris = new ObservableCollection<string>();

this.Start.IsEnabled = false;

try

Formatted: Font: Bold

Page 14: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

{

...

Visual Basic

Private Async Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

Dim uris As New List(Of String)()

Dim uris As New ObservableCollection(Of String)()

Me.Start.IsEnabled = False

4. To improve the application responsiveness, you will now use a Task to run the code that finds

links in the document. This will move that work to a thread pool thread, ensuring that the UI

thread remains responsive even if that code takes a long time to run.

Enclose the filter and list manipulation logic into a lambda expression, which will run on

background thread when calling Task.Run. This Start_Click method needs to wait for this task to

complete, because it needs to put the resulting data into a ListBox, but it will perform that wait

asynchronously, using the await keyword.

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

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

...

try

{

...

this.textBox1.Text = result;

await Task.Run(() =>

{

MatchCollection mc = Regex.Matches(result,

"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

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

}

});

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

Visual Basic

Private Async Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

Page 15: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

...

Try

...

Me.textBox1.Text = result

Await Task.Run(Sub()

Dim mc As MatchCollection = Regex.Matches(result,

"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)

For Each m As Match In mc

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

Next

End Sub)

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

pool. However, the await ensures that once this task completes, any further work will resume

back on the UI thread. That is important here, because the Click handler method goes on to

update a ListBox, and it is illegal to do that from any thread other than the UI thread.

5. 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. Replace the LinkInfo class with the following code.

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

C#

namespace AsyncLab

{

public class LinkInfo

{

public string Title { get; set; }

public string Html { get; set; }

public int Length { get; set; }

}

}

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

Visual Basic

Public Class LinkInfo

Public Property Title() As String

Public Property Html() As String

Public Property Length As Integer

End Class

Page 16: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

6. Create a new method to download a page in the MainWindow.xaml.cs or

MainWindow.xaml.vb code-behind class. Name this method DownloadItemAsync and declare

it with the async prefix. This method will return a LinkInfo with an empty string should the HTTP

GET operation fail. The method GetTtitle wil be implemented in the next step.

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

C#

private static async Task<LinkInfo> DownloadItemAsync(Uri itemUri)

{

string item;

try

{

HttpClient httpClient = new HttpClient();

httpClient.MaxResponseContentBufferSize = 1000000;

var response = await httpClient.GetAsync(itemUri);

item = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

}

catch

{

item = string.Empty;

}

LinkInfo linkInfo = new LinkInfo { Length = item.Length, Title =

GetTitle(item), Html = item };

return linkInfo;

}

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

Visual Basic

Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri) As

Task(Of LinkInfo)

Dim item As String

Try

Dim httpClient As New HttpClient

httpClient.MaxResponseContentBufferSize = 1000000

Dim response = Await httpClient.GetAsync(itemUri)

item = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

Catch

item = String.Empty

End Try

Formatted: Font: Bold

Page 17: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Dim linkInfo As LinkInfo = New LinkInfo() With {.Length = item.Length, .Html

= item, .Title = GetTitle(item)}

Return linkInfo

End Function

Note: Notice we are setting a relatively large HTTP client buffer size. This is necessary because

default value is about 65Kb, and many web pages are larger than this, so downloading them

would generate an application error if we had not increased the buffer size.

7. Create a GetTitle method after the DownloadItemAsync method to return the text located

inside the title tag.

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

C#

private static string GetTitle(string html)

{

if (html.Length == 0)

{

return "Not Found";

}

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

RegexOptions.IgnoreCase);

return m.Value;

}

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

Visual Basic

Private Shared Function GetTitle(ByVal html As String) As String

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

Return "Not Found"

End If

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

RegexOptions.IgnoreCase)

Return m.Value

End Function

Page 18: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

8. Modify the Start_Click event handler logic to use the DownloadItemAsync method and

populate the ListBox with the retrieved items.

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

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

...

try

{

...

await Task.Run(() =>

{

MatchCollection mc = Regex.Matches(result,

"href\\s*=\\s*(?:\"(?<1>http://[^\"]*)\")", RegexOptions.IgnoreCase);

foreach (Match m in mc)

{

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

}

});

this.listBox1.ItemsSource = await Task.WhenAll(from uri in uris select

DownloadItemAsync(new Uri(uri)));

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

}

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

Visual Basic

Private Async Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

...

Try

...

Await Task.Run(Sub()

Dim mc As MatchCollection = Regex.Matches(result,

"href\s*=\s*(?:\""(?<1>http://[^""]*)\"")", RegexOptions.IgnoreCase)

For Each m As Match In mc

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

Next

End Sub)

Page 19: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Me.listBox1.ItemsSource = Await Task.WhenAll( _

From uri In uris _

Select DownloadItemAsync(New Uri(uri)))

Catch ex As Exception

MessageBox.Show(ex.ToString())

End Try

...

End Sub

Note:

- The Task.WhenAll method takes a collection of tasks as input, and returns a new task that

completes only when all of the tasks you passed in have completed. The result of the task is an

array containing each of the results from the constituent tasks. This example uses the await

keyword on the task returned by Task.WhenAll, so the effect is that the code will perform an

asynchronous wait for that task (using callbacks under the covers), and the statement will only

complete when every task has finished, at which point it writes the resulting array into the list

box’s ItemsSource property.

- There is also a Task.WhenAny method (not used here). That returns a task that completes as

soon as at least one of the constituent tasks completes.

9. Open MainWindow.xaml XAML view and insert the following code before the Grid. This is a

template that you will use to show the link title and the length of each linked page.

XAML

<Window x:Class="MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

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

<Window.Resources>

<DataTemplate x:Key="DataTemplateItem">

<StackPanel Orientation="Horizontal">

<TextBlock>

<TextBlock.Text>

<MultiBinding StringFormat=" {0} - {1} ">

<Binding Path="Length"/>

<Binding Path="Title"/>

</MultiBinding>

</TextBlock.Text>

</TextBlock>

</StackPanel>

</DataTemplate>

</Window.Resources>

<Grid>

Page 20: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

...

10. 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="{DynamicResource DataTemplateItem}"/>

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

Margin="272,406,0,0" Name="Start" VerticalAlignment="Top" Width="75"

Click="Start_Click" />

</Grid>

Task 2 – Verification

1. Press F5 to run the application.

2. Click the Start button to begin downloading the linked pages. The left-hand pane should show

the HTML for the initial web page reasonably quickly. The list box on the right will take longer to

populate. (Even though the code fetches multiple pages concurrently, it will still have to

download over 100 pages, so depending on how fast your Internet connection is, this could take

a while.) Note that the application is still responsive while the downloads execute in the

background.

3. After all of the downloads complete, the application will show the list of pages with their length

and title.

Page 21: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Figure 2

Running the application

Exercise 3: Polling and Cancellation

In this exercise, you will add additional features to the application. First, you will introduce the ability to

cancel the work that is running in the background. Then, you will add the ability to see when each of the

pages the main page refers to changes. (You will do this by polling – downloading each page repeatedly

to check for changes.) If the web site offers different content than the last time, you will notify the user

by changing the link color in the ListBox.

Task 1 – Adding the Cancellation Feature

In this task, you will implement the cancellation feature to terminate downloads in progress.

1. Open Visual Studio 2013 and load the AsyncLab-Ex3-Begin.sln solution located on your desktop

in the folder TechEd 2014\Asynchronous Programming in the .NET

Framework\Source\[CS|VB]\Ex2-Concurrency\Begin.solution located in the

Source\[CS|VB]\Ex3-PollingAndCancellation\Begin folder of this lab. Alternatively, you can

continue working with the solution you created in Exercise 2, if you chose to follow that to the

end.

2. Open MainWindow.xaml.cs or MainWindow.xaml.vb and import the System.Threading

namespace.

Page 22: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

C#

using System.Threading;

Visual Basic

Imports System.Threading

3. Define a class field to hold the cancellation token.

(Code Snippet – Async Lab - Ex03 - Cancellation Token - CS)

C#

public partial class MainWindow : Window

{

private CancellationTokenSource cancellationToken;

(Code Snippet – Async Lab - Ex03 - Cancellation Token - VB)

Visual Basic

Class MainWindow

Private cancellationToken As CancellationTokenSource

4. Modify the DownloadItemAsync method to receive a CancellationToken parameter.

C#

private static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,

CancellationToken cancellationToken)

{

Visual Basic

Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal

cancellationToken As CancellationToken) As Task(Of LinkInfo)

5. Create a new instance of the cancellation token inside the Start_Click event handler.

(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - CS)

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

this.cancellationToken = new CancellationTokenSource();

ObservableCollection<string> uris = new ObservableCollection<string>();

Page 23: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

(Code Snippet – Async Lab - Ex03 - Cancellation Token Instance - VB)

Visual Basic

Private Async Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

Me.cancellationToken = New CancellationTokenSource()

Dim uris As New ObservableCollection(Of String)()

6. In the Start_Click method, modify the HttpClient call to include a cancellation token. To do this,

replace the GetAsync call with two lines, the first of which creates a request message object,

and the second of which uses the SendAsync method, which supports cancellation tokens.

(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - CS)

C#

private async void Start_Click(object sender, RoutedEventArgs e)

{

this.cancellationToken = new CancellationTokenSource();

ObservableCollection<string> uris = new ObservableCollection<string>();

this.Start.IsEnabled = false;

try

{

var message = new HttpRequestMessage(HttpMethod.Get,

"http://msdn.microsoft.com");

var response = await new HttpClient().SendAsync(message,

this.cancellationToken.Token);

string result = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

this.textBox1.Text = result;

...

(Code Snippet – Async Lab - Ex03 - StartClick with CancellationToken - VB)

Visual Basic

Private Async Sub Start_Click(sender As System.Object, e As

System.Windows.RoutedEventArgs) Handles Start.Click

Me.cancellationToken = New CancellationTokenSource()

Dim uris As New ObservableCollection(Of String)()

Me.Start.IsEnabled = False

Try

Dim message = New HttpRequestMessage(HttpMethod.Get,

"http://msdn.microsoft.com")

Page 24: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Dim response = Await (New HttpClient()).SendAsync(message,

Me.cancellationToken.Token)

Dim result As String = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

Me.textBox1.Text = result

...

7. Likewise in the DownloadItemAsync method, change the HttpClient.GetAsync call with an

HttpClient.SendAsync call to include a cancellation token.

(Code Snippet – Async Lab - Ex03 - DownloadItemAsync with CancellationToken - CS)

C#

public static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,

CancellationToken cancellationToken)

{

string item;

try

{

HttpClient httpClient = new HttpClient();

httpClient.MaxResponseContentBufferSize = 1000000;

var message = new HttpRequestMessage(HttpMethod.Get, itemUri);

var response = await httpClient.SendAsync(message, cancellationToken);

item = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

...

(Code Snippet – Async Lab - Ex03 - DownloadItemAsync with CancellationToken - VB)

Visual Basic

Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal

cancellationToken As CancellationToken) As Task(Of LinkInfo)

Dim item As String

Try

Dim httpClient As New HttpClient

httpClient.MaxResponseContentBufferSize = 1000000

Dim message = New HttpRequestMessage(HttpMethod.Get, itemUri)

Dim response = Await httpClient.SendAsync(message, cancellationToken)

item = Await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

...

Page 25: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

8. In the Start_Click method, modify call to the DownloadItemAsync to pass the cancellation

token. Replace the ListBox’s ItemsSource property assignment with the following code:

(Code Snippet – Async Lab - Ex03 - Bind ListBox To Link Info - CS)

C#

this.listBox1.ItemsSource = await Task.WhenAll(from uri in uris select

DownloadItemAsync(new Uri(uri), this.cancellationToken.Token));

(Code Snippet – Async Lab - Ex03 - Bind ListBox To Link Info - VB)

Visual Basic

Me.listBox1.ItemsSource = Await Task.WhenAll( _

From uri In uris _

Select DownloadItemAsync(New

Uri(uri), Me.cancellationToken.Token))

9. In the Start_Click try-catch block, add a new handler to catch the OperationCanceledException

exception. Show a message box to tell the user that the operation was cancelled.

(Code Snippet – Async Lab - Ex03 - OperationCanceledException Handler - CS)

C#

}

catch (OperationCanceledException)

{

MessageBox.Show("Operation Cancelled");

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

(Code Snippet – Async Lab - Ex03 - OperationCanceledException Handler - VB)

Visual Basic

Catch exCancel As OperationCanceledException

MessageBox.Show("Operation Cancelled")

Catch ex As Exception

MessageBox.Show(ex.ToString())

End Try

Page 26: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

10. Open MainWindow.xaml Design View. Add a new button and set the name and the content to

Cancel. Modify the margin of the first button to ensure that the buttons do not overlap. Your

XAML in MainWindow.xaml should look something like this after you add the button:

XAML

<Grid>

...

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

Margin="272,406,0,0" Name="Start" VerticalAlignment="Top" Width="75"

Click="Start_Click" />

<Button Content="Cancel" Height="23" HorizontalAlignment="Left"

Margin="360,406,0,0" Name="Cancel" VerticalAlignment="Top" Width="75" />

</Grid>

</Window>

11. Double-click the Cancel button to add an event handler.

12. In the Cancel_Click event handler, call the cancellation token’s Cancel method.

(Code Snippet – Async Lab - Ex03 - Cancel - CS)

C#

private void Cancel_Click(object sender, RoutedEventArgs e)

{

this.cancellationToken.Cancel();

}

(Code Snippet – Async Lab - Ex03 - Cancel - VB)

Visual Basic

Private Sub Cancel_Click(sender As Object, e As RoutedEventArgs) Handles

Cancel.Click

Me.cancellationToken.Cancel()

End Sub

13. Press F5 to run the application, and then, click the Start button.

14. Press the Cancel button. Notice you can now cancel the operation before it completes. The list

on the right will only show content for the links that it had finished downloading before the

cancellation occurred.

Note: The code that fetches the content for each link does not currently make a distinction

between different kinds of errors—if it started an attempt to download a link’s content, and

Page 27: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

that attempt then fails for any reason, the application reports “Not found”. It does this even if

the attempt failed due to cancellation.

So when you click the Cancel button, you will still see plenty of items in the listbox, but it will

show “No content” for each of the download operations that were cancelled.

Task 2 – Polling the Linked Pages to Detect Changes

In this task, you will add code that downloads the content for each of the links repeatedly to detect

changes. It will change the color of the corresponding list entry any time the content referred to by a

link has a different size from the content fetched when you initially click Start.

Note: since this step repeatedly fetches all of the content for everything that the MSDN home

page links too (typically over a hunred pages), this will consume a fair amount of bandwidth.

So if you’re on a connection with a limited download allowance (as is usually the case with

cellular data plans, and also on some broadband packages) you should not leave the program

running for long.

1. Replace all the code of LinkInfo class with the following code. You will implement

INotifyPropertyChanged, which will enable WPF to update bindings automatically each time a

property changes. Additionally, you will be adding a new property named Color.

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

C#

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading;

using System.ComponentModel;

using System.Windows.Media;

namespace AsyncLab

{

public class LinkInfo : INotifyPropertyChanged

{

private string html;

private string title;

private int length;

private Color color;

Page 28: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

public event PropertyChangedEventHandler PropertyChanged;

public string Title

{

get

{

return this.title;

}

set

{

this.title = value;

this.NotifyPropertyChanged("Title");

}

}

public string Html

{

get

{

return this.html;

}

set

{

this.html = value;

this.NotifyPropertyChanged("Html");

}

}

public int Length

{

get

{

return this.length;

}

set

{

this.length = value;

this.NotifyPropertyChanged("Length");

}

}

public Color Color

{

get

{

return this.color;

Page 29: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

}

set

{

this.color = value;

this.NotifyPropertyChanged("Color");

}

}

private void NotifyPropertyChanged(string propertyName)

{

if (this.PropertyChanged != null)

{

this.PropertyChanged(this, new

PropertyChangedEventArgs(propertyName));

}

}

}

}

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

Visual Basic

Imports System.ComponentModel

Imports System.Windows.Media

Public Class LinkInfo

Implements INotifyPropertyChanged

Private mHtml As String

Private mTitle As String

Private mLength As Integer

Private mColor As Color

Public Event PropertyChanged(sender As Object, e As

PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

Public Property Html() As String

Get

Return Me.mHtml

End Get

Set(ByVal value As String)

Me.mHtml = value

Me.NotifyPropertyChanged("Html")

End Set

End Property

Public Property Title() As String

Page 30: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Get

Return Me.mTitle

End Get

Set(ByVal value As String)

Me.mTitle = value

Me.NotifyPropertyChanged("Title")

End Set

End Property

Public Property Length() As Integer

Get

Return Me.mLength

End Get

Set(ByVal value As Integer)

Me.mLength = value

Me.NotifyPropertyChanged("Length")

End Set

End Property

Public Property Color() As Color

Get

Return Me.mColor

End Get

Set(ByVal value As Color)

Me.mColor = value

Me.NotifyPropertyChanged("Color")

End Set

End Property

Private Sub NotifyPropertyChanged(propertyName As String)

RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

End Sub

End Class

2. In the MainWindow.xaml file, modify the DataTemplate resource to include set the TextBlock’s

Background property using a SolidColorBrush with its Color attribute to the LinkInfo class’s

Color property.

XAML

<Window.Resources>

<DataTemplate x:Key="DataTemplateItem">

<StackPanel Orientation="Horizontal">

<TextBlock>

<TextBlock.Background>

<SolidColorBrush Color="{Binding Color}" />

</TextBlock.Background>

<TextBlock.Text>

Page 31: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

3. Open the MainWindow.xaml.cs or MainWindows.xaml.vb file and add an async method called

PollItem. It will receive a Uri, a LinkInfo and a CancellationToken.

(Code Snippet – Async Lab - Ex03 - PollInfo Method - CS)

C#

private static async void PollItem(Uri itemUri, LinkInfo link,

CancellationToken cancellationToken)

{

}

(Code Snippet – Async Lab - Ex03 - PollInfo Method - VB)

Visual Basic

Private Shared Async Sub PollItem(ByVal itemUri As Uri, ByVal link As

LinkInfo, ByVal cancellationToken As CancellationToken)

End Sub

4. Implement the PollItem method to poll the URI every 5 seconds and check for changes in the

content. Each time a change is found, it updates the LinkInfo by changing the color property

with a random color.

(Code Snippet – Async Lab - Ex03 - PollInfo Implementation - CS)

C#

private static async void PollItem(Uri itemUri, LinkInfo link,

CancellationToken cancellationToken)

{

Random r = new Random();

HttpClient httpClient = new HttpClient();

httpClient.MaxResponseContentBufferSize = 1000000;

try

{

while (true)

{

await Task.Delay(5000, cancellationToken);

var requestMessage = new HttpRequestMessage(HttpMethod.Get, itemUri);

var response = await httpClient.SendAsync(requestMessage,

cancellationToken);

string item = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();

if (item.Length != link.Length)

Page 32: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

{

link.Title = GetTitle(item);

link.Length = item.Length;

link.Html = item;

link.Color = Color.FromArgb((byte)255, (byte)r.Next(256),

(byte)r.Next(256), (byte)r.Next(256));

}

}

}

catch

{

}

}

(Code Snippet – Async Lab - Ex03 - PollInfo Implementation - VB)

Visual Basic

Private Shared Async Sub PollItem(ByVal itemUri As Uri, ByVal link As

LinkInfo, ByVal cancellationToken As CancellationToken)

Dim r As New Random()

Dim httpClient As New HttpClient

httpClient.MaxResponseContentBufferSize = 1000000

Try

Do

Await Task.Delay(5000, cancellationToken)

Dim requestMessage = New HttpRequestMessage(HttpMethod.Get, itemUri)

Dim response = Await httpClient.SendAsync(requestMessage,

cancellationToken)

Dim item As String = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync()

If item.Length <> link.Length Then

link.Title = GetTitle(item)

link.Length = item.Length

link.Html = item

link.Color = Color.FromArgb(Convert.ToByte(255),

Convert.ToByte(r.Next(256)), Convert.ToByte(r.Next(256)),

Convert.ToByte(r.Next(256)))

End If

Loop

Catch

End Try

End Sub

Page 33: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Note: Task.Delay is the asynchronous version of Thread.Sleep. It returns a Task object that

completes after the specified amount of time. This enables you to wait for a specified length of

time, but unlike Thread.Sleep, this will not block a thread for the duration.

5. In the DownloadItemAsync function, add a call to PollItem.

(Code Snippet – Async Lab - Ex03 - PollInfo Call - CS)

C#

public static async Task<LinkInfo> DownloadItemAsync(Uri itemUri,

CancellationToken cancellationToken)

{

...

LinkInfo linkInfo = new LinkInfo { Length = item.Length, Title =

GetTitle(item), Html = item };

PollItem(itemUri, linkInfo, cancellationToken);

return linkInfo;

}

(Code Snippet – Async Lab - Ex03 - PollInfo Call - VB)

Visual Basic

Private Shared Async Function DownloadItemAsync(ByVal itemUri As Uri, ByVal

cancellationToken As CancellationToken) As Task(Of LinkInfo)

...

Dim linkInfo As LinkInfo = New LinkInfo() With {.Length = item.Length, .Html

= item, .Title = GetTitle(item)}

PollItem(itemUri, linkInfo, cancellationToken)

Return linkInfo

End Function

6. Press F5 to run the application, and then click the Start button. Notice the background color of

the list items change when the application detects an update. Commented [A1]: I am not seeing updates after several minutes. How long should I wait?

Page 34: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Figure 3

Polling pages for content changes

Note: The list may take a while to populate. This is due to the use of Task.Delay in the PollItem

method. Shorten the delay if you think it is taking too long.

Exercise 4: Async Library Refactoring

In this exercise, you will refactor the solution in order to move the asynchronous logic to a class library,

using the proper naming conventions for asynchronous methods that conform to the TAP. Additionally,

you will improve the performance by avoiding switching back to the User Interface (UI) thread when it is

not necessary.

Task 1 – Creating an Asynchronous Class Library

1. Open Visual Studio 2013 and load AsyncLab-Ex4-Begin.sln solution located on your desktop in

the folder TechEd 2014\Asynchronous Programming in the .NET

Framework\Source\[CS|VB]\Ex2-Concurrency\Begin.solution located in the

Source\[CS|VB]\Ex4-Refactoring\Begin folder of this lab. Alternatively, you can continue

working with the solution you created in Exercise 3.

Page 35: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

2. Create a new Class Library project in the same solution. Name the new project

AsyncLabLibrary. Then, delete Class1 (which Visual Studio creates automatically when you add

a new class library project).

3. Add a reference to PresentationCore and System.Net.Http.

4. Copy the LinkInfo class from the AsyncLab project into the AsyncLabLibrary library, and then

delete the original LinkInfo file from the AsyncLab project.

5. If you are using C#, rename the namespace in the LinkInfo.cs file from AsyncLab to

AsyncLabLibrary.

6. Add a reference to the AsyncLabLibrary library in the AsyncLab project.

7. In AsyncLabLibrary project, add a new class and name it Downloader.

8. If you are using C#, add a public class access modifier to the Downloader class.

9. Include the following namespace directives:

(Code Snippet – Async Lab - Ex04 - References - CS)

C#

using System.Net.Http;

using System.Text.RegularExpressions;

using System.Threading;

using System.Windows.Media;

(Code Snippet – Async Lab - Ex04 - References - VB)

Visual Basic

Imports System.Net.Http

Imports System.Text.RegularExpressions

Imports System.Threading

Imports System.Windows.Media

Task 2 – Refactoring the Project

1. Open MainWindow.xaml.cs or MainWindow.xaml.vb from the AsyncLab project.

2. Move the DownloadItemAsync, PollItem and GetTitle methods from the MainWindow.xaml.cs

or MainWindow.xaml.vb file to the Downloader class.

3. Rename the DownloadItemAsync method to DownloadItemAsyncInternal. If you are working

in C#, use Visual Studio Refactoring to rename any reference to the method.

4. Rename the PollItem method to PollItemAsync. (This is a public method, and public methods

that conform to the TAP should end with Async.) If you are working in C#, use Visual Studio

Page 36: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Refactoring feature to rename any reference to the method. Otherwise, rename the PollItem

method call to PollItemAsync in the DownloadItemAsync method.

5. Create a DownloadItemAsync public method. The method will receive an URI and a

CancellationToken to support cancellation and call DownloadItemAsyncInternal.

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

C#

public static Task<LinkInfo> DownloadItemAsync(Uri itemUri, CancellationToken

cancellationToken)

{

if (itemUri == null)

{

throw new ArgumentNullException("itemUri");

}

return DownloadItemAsyncInternal(itemUri, cancellationToken);

}

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

Visual Basic

Public Shared Function DownloadItemAsync(ByVal itemUri As Uri, ByVal

cancellationToken As CancellationToken) As Task(Of LinkInfo)

If itemUri Is Nothing Then

Throw New ArgumentNullException("itemUri")

End If

Return DownloadItemAsyncInternal(itemUri, cancellationToken)

End Function

6. Create an overload method for DownloadItemAsync without the cancellation token parameter.

(Code Snippet – Async Lab - Ex04 - DownloadItemAsync No Cancellation - CS)

C#

public static Task<LinkInfo> DownloadItemAsync(Uri itemUri)

{

return DownloadItemAsync(itemUri, CancellationToken.None);

}

(Code Snippet – Async Lab - Ex04 - DownloadItemAsync No Cancellation - VB)

Visual Basic

Page 37: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Public Shared Function DownloadItemAsync(ByVal itemUri As Uri) As Task(Of

LinkInfo)

Return DownloadItemAsync(itemUri, CancellationToken.None)

End Function

7. Modify the DownloadItemAsyncInternal method to call the ConfigureAwait method at the end

of SendAsync call, passing an argument of false. Add a similar call to the ReadAsStringAsync call

on the next line.

(Code Snippet – Async Lab - Ex04 - DownloadItemAsyncInternal ConfigureAwait- CS)

C#

var message = new HttpRequestMessage(HttpMethod.Get, itemUri);

var response = await httpClient.SendAsync(message,

cancellationToken).ConfigureAwait(false);

item = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(fals

e);

(Code Snippet – Async Lab - Ex04 - DownloadItemAsyncInternal ConfigureAwait - VB)

Visual Basic

Dim message = New HttpRequestMessage(HttpMethod.Get, itemUri)

Dim response = Await httpClient.SendAsync(message,

cancellationToken).ConfigureAwait(False)

item = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(Fals

e)

Note: If you use the await keyword on the UI thread, by default it ensures that when the

awaited operation completes, the remainder of the method runs on the UI thread (even in

situations where the asynchronous work ended up using thread pool threads). If this is not

needed, call ConfigureAwait(false). This can improve performance because it allows work to

continue on some other thread if that would be more efficient. (The default behavior of

completing the method on the UI thread can cause two problems: 1) if the UI thread is already

busy, the work will be delayed, 2) if the rest of the method is slow, running it on the UI thread

will reduce the application’s responsiveness.)

8. Modify the PollItemAsync method to call ConfigureAwait at the end of the SendAsync call,

passing an argument of false. Add a similar call to the ReadAsStringAsync call on the next line.

(Code Snippet – Async Lab - Ex04 - PollItem ConfigureAwait- CS)

Page 38: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

C#

private async void PollItemAsync(Uri itemUri, LinkInfo link, CancellationToken

cancellationToken)

{

Random r = new Random();

HttpClient httpClient = new HttpClient();

httpClient.MaxResponseContentBufferSize = 1000000;

try

{

while (true)

{

await Task.Delay(5000, cancellationToken);

var requestMessage = new HttpRequestMessage(HttpMethod.Get, itemUri);

var response = await httpClient.SendAsync(requestMessage,

cancellationToken).ConfigureAwait(false);

string item = await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(

false);

if (item.Length != link.Length)

{

link.Title = GetTitle(item);

link.Length = item.Length;

link.Html = item;

link.Color = Color.FromArgb((byte)255, (byte)r.Next(255),

(byte)r.Next(255), (byte)r.Next(255));

}

}

}

catch

{

}

}

(Code Snippet – Async Lab - Ex04 - PollItem ConfigureAwait - VB)

Visual Basic

Private Async Sub PollItemAsync(ByVal itemUri As Uri, ByVal link As LinkInfo,

ByVal cancellationToken As CancellationToken)

Dim r As New Random()

Dim httpClient As New HttpClient

httpClient.MaxResponseContentBufferSize = 1000000

Try

Do

Await Task.Delay(5000, cancellationToken)

Dim requestMessage = New HttpRequestMessage(HttpMethod.Get, itemUri)

Page 39: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Dim response = Await httpClient.SendAsync(requestMessage,

cancellationToken).ConfigureAwait(False)

Dim item As String = Await

response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(

False)

If item.Length <> link.Length Then

link.Title = GetTitle(item)

link.Length = item.Length

link.Html = item

link.Color = Color.FromArgb(Convert.ToByte(255),

Convert.ToByte(r.Next(255)), Convert.ToByte(r.Next(255)),

Convert.ToByte(r.Next(255)))

End If

Loop

Catch

End Try

End Sub

9. Open MainWindow.xaml.cs or MainWindow.xaml.vb code-behind file and import the

AsyncLabLibrary namespace.

(Code Snippet – Async Lab - Ex04 - References dll - CS)

C#

using AsyncLabLibrary;

(Code Snippet – Async Lab - Ex04 - References dll - VB)

Visual Basic

Imports AsyncLabLibrary

10. In the Start_Click method, replace the ListBox polling code to call the new DownloadItemAsync

method from the downloader library.

(Code Snippet – Async Lab - Ex04 - DownloadItemAsync Call - CS)

C#

this.listBox1.ItemsSource = await

Task.WhenAll(from uri in uris select Downloader.DownloadItemAsync(new

Uri(uri), this.cancellationToken.Token));

(Code Snippet – Async Lab - Ex04 - DownloadItemAsync Call - VB)

Visual Basic

Page 40: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Me.listBox1.ItemsSource = Await Task.WhenAll( _

From uri In uris _

Select

Downloader.DownloadItemAsync(New Uri(uri), Me.cancellationToken.Token))

11. Press F5 to run the application. You will notice no difference in the visible behavior. The UI is

still responsive while the application downloads content for links. ConfigureAsync provides a

performance improvement, as it avoids marshaling control back to the UI thread once

asynchronous operations complete (although in practice, you are unlikely to be able to notice

any difference in the visible performance of this application).

Appendix: Using Code Snippets

With code snippets, you have all the code you need at your fingertips. The lab document will tell you

exactly when you can use them, as shown in the following figure.

Figure 1

Using Visual Studio code snippets to insert code into your project

1. Right-click where you want to insert the code snippet.

2. Select Insert Snippet followed by My Code Snippets.

3. Pick the relevant snippet from the list, by clicking on it.

Page 41: Hands-On Lab · solutions and that they will not necessarily work until you complete the exercise. Inside the folder for each exercise, you will also find an End folder containing

Figure 2

Right-click where you want to insert the code snippet and select Insert Snippet

Figure 3

Pick the relevant snippet from the list, by clicking on it