CHAPTER 2

image

Introduction to Asynchronous Programming in .NET

Asynchronous programming is generally one of the hardest programming concepts to grasp. It is even harder to understand what it means when it comes to dealing with server applications, notably web applications. Besides the general concept, the asynchronous programming model in the .NET Framework was also hard to grasp in the early days of the framework. With the introduction of Task Parallel Library (TPL) in .NET version 4.0, it became easier, but the programming model was still not ideal. With the new asynchronous programming language features of C# in .NET 4.5, it got easier still. It now may be said to offer the ideal programming model as well.

In terms of both infrastructure and extensibility points, ASP.NET Web API framework is asynchronous from top to bottom by design. In addition, it leverages the Task-based Asynchronous Pattern (or TAP) introduced in .NET 4.0 to enable this. As we will be dealing with asynchrony throughout the book, it would be helpful to ensure up front that there is a good understanding of asynchronous programming and TAP. So this chapter will focus on asynchronous programming in .NET and, especially at the server level, with IIS Web Server, which is the most commonly used hosting platform for ASP.NET Web API applications.

What Is It All About?

Put very simply, “asynchronous code” refers to code in an operation (which the code executes) where that code won’t wait till the operation completes; therefore, it doesn’t block the thread it is running on. The need for asynchronous programming comes from the fact that a long-running operation blocks the thread that is executing it until the operation completes. If such an operation is performed synchronously, the result will be an unresponsive software application—one that might risk the health of your entire application. This may sound a little abstract, so let’s use a scenario from daily life to make sense of it.

Assume that a few days ago, after I bought a product from Company A, I began having a problem with it. I called the company’s support center to explain and sort out the problem. After listening to my explanation, the customer service representative asked me to wait a few minutes. While he tried to solve the problem, I was left hanging on the telephone. The important part here is that I couldn’t do anything else until the representative got back to me. Any other tasks I needed to perform were, like me, left on hold while I waited for my call to end.

My situation in this scenario can be related to synchronous processing of a long-running operation (Figure 2-1).

9781430247258_Fig02-01.jpg

Figure 2-1. A synchronous phone call between me and Company A’s customer service representative

Let’s construct another scenario. This time I bought a product from Company B; again, there was a problem. I called Company B’s support center and explained the problem to a customer service representative. This time, however, since the representative said she would call me back as soon as the problem got sorted out, I could hang up the phone. This allowed me to see to other tasks while Company B’s people worked on my problem. Later, the representative called me back and informed me of the problem’s resolution. This scenario resembles how an asynchronous operation works (see Figure 2-2).

9781430247258_Fig02-02.jpg

Figure 2-2. An asynchronous phone call between me and Company B’s customer service representative

It is pretty easy to get confused about asynchronous programming, but it is even easier to get confused about leveraging asynchrony on server applications. The advantage of asynchronous programming is easily recognizable on client applications such as WPF, Windows Forms or Windows Store applications. Let’s look at an example. In a WPF application, for instance, we want to download a web page (let’s say www.apress.com) and insert its HTML code as text somewhere in our window. If we try to do this synchronously (in other words, on the same thread with the UI), the UI will stay unresponsive until the download operation is completed. It’s certain that all of us have seen “(Not Responding)” at the top of the window at least a couple of times. The same situation will occur in this example if we try to move the window around because the UI thread is busy and cannot do anything else. On the other hand, if we try to perform the same operation asynchronously, our UI thread will be freed up and stay responsive.

There’s one important thing to keep in mind in our sample here. No matter whether the operation is performed asynchronously or not, the user has to wait for the same amount of time to see the result. Though you may well think that this makes asynchrony undesirable for server applications, you would be wrong. Keep the phone conversation example in mind as we consider how requests are processed in IIS web server and how we can take advantage of asynchrony.

IIS Web Server and Asynchronous Processing

IIS Web Server offers a flexible, secure, and easy-to-manage Web server for hosting anything on the Web. When an ASP.NET application is hosted under IIS, it uses a CLR thread pool to manage requests and sends HTTP responses back one at a time because a thread cannot serve multiple requests simultaneously.

This section will show how asynchronous processing improves scaling and helps several IIS configurations get the best out of IIS.

IIS Web Server Processing Architecture in a Nutshell

IIS reserves a collection of free threads inside CLR’s thread pool. These threads serve by responding to requests made to the IIS web server. By nature, a thread from the thread pool can respond to only one request at a time. This is not an issue most of the time, but it may well be if you start to get more traffic over time.

This structure will also cause problems with scaling if there is an application processing multiple long-running operations. This type of application will block the thread the operation is running on. As the number of requests increases, the number of free threads to handle requests inside the CLR’s thread pool will decrease. This problem, also known as Thread Starvation , can affect the responsiveness of the application drastically.

In an asynchronous processing structure, threads are not blocked while waiting for operations to complete. When an operation starts (a network call, a file system operation, etc.), the web server hands the request to a free thread inside the thread pool. If this operation is long running and is processed asynchronously, the action informs the thread that the operation will take time and that it will inform the IIS when it is completed. Then the thread will return to the thread pool, where it will be able to handle other requests while the operation is waiting for a response. As soon as the running operation is completed and the data are ready, the action will inform the IIS about the operation’s status. From that point, IIS will grasp a free thread from the thread pool and assign it to this operation. Note that the thread assigned to complete the processing of that request might or might not be the thread that started processing the request. This situation is processed by IIS, and the application itself is not concerned about it. In other words the application does not concern itself about which thread the operation runs on. It cares only about the result.

Another important point, one not to be overlooked, is that no matter how the operation is being processed, the amount of time the operation needs for completion will be approximately the same. One of the common misconceptions of asynchrony in .NET is that it allows an individual asynchronous operation to complete faster than the synchronous version. The positive side of asynchronous processing is that asynchronous operations will not block the thread they run on, whereas normal synchronous operations will. What’s more, you may run two asynchronous operations at the same time in parallel; doing so will cut the time the two operations need to complete. We will see how this benefits us in our ASP.NET Web API applications.

IIS and Server Configuration for High Concurrency

For asynchronous processing in IIS 7.5 and 8.0, running with default configuration works best in general, but in some cases you might need to make some changes to your default server and IIS settings in order to get the most out of it.

With .NET Framework 4.0, the default configuration settings are suitable for most scenarios. For example, in ASP.NET 4.0 and 4.5, the MaxConcurrentRequestsPerCPU value is set to 5000 by default (it had been a very low number in earlier versions of .NET). According to team members of IIS, there’s nothing special about the value 5000. It was set because it is a very large number and will allow plenty of async requests to execute concurrently. This setting should be fine, so there is no need to change it.

image Tip  Windows 7, Windows Vista, and all other Windows client operating systems handle a maximum of 10 concurrent requests. You need to have a Windows Server operating system or use IIS Express to see the benefits of asynchronous methods under high load.

Even if a large number of concurrent requests per CPU are allowed, other factors can still limit effective asynchronous processing. There is an attribute named queueLength that indicates to HTTP.sys how many requests to queue for an application pool before rejecting future requests. HTTP.sys (hypertext transfer protocol stack) is the HTTP listener that is implemented as a kernel-mode device driver in order to listen for HTTP requests from the network, pass the requests onto IIS for processing, and then return processed responses to client browsers. The default value for the HTTP.sys queue limit is 1000. If this setting is too low for the application pool, HTTP.sys will reject requests with an HTTP 503 status, which indicates that the server is unavailable for any request to process. The HTTP.sys kernel queue limit is controlled by IIS and can be configured per application pool. To configure the HTTP.sys kernel queue limit for a particular application pool, open the IIS Manager and navigate to the Application Pools node (see Figure 2-3).

9781430247258_Fig02-03.jpg

Figure 2-3. The Application Pools node in the IIS Manager

Inside the middle section of the IIS Manager window that shows the application pools, right-click on the application pool you would like to configure and select Advanced Settings (see Figure 2-4).

9781430247258_Fig02-04.jpg

Figure 2-4. The Advanced Settings option of an IIS application pool

In the Advanced Settings dialog box (see Figure 2-5), find the Queue Length option under General node. This should be set to 1000 as default. Changing Queue Length from 1000 to 5000 should be fine.

9781430247258_Fig02-05.jpg

Figure 2-5. The Queue Length option in the Advanced Settings dialog box

These IIS and server settings are general, but some other settings are specific to the asynchronous operation type. You will see some of them later in Chapter 16 when use cases and scenarios are discussed.

Where to Favor Asynchronous Programming

Although operations performed asynchronously can make applications much more responsive, this isn’t the case for every scenario. Because asynchronous programming is much more complex than normal synchronous programming, even if C# 5.0 language features make it easier to implement, the processing logic is still complex and thus hard to get right.

A specific case where asynchrony doesn’t provide a performance benefit is where operations are primarily CPU operations. These shouldn’t be performed asynchronously because asynchrony provides no advantages and results in more overhead by incurring more resource usage and an expensive thread switch.

image Tip  Task Parallel Library (TPL) also allows for parallel processing. If you have a scenario in which you need to kick off a CPU-bound task and this operation can be split into discrete tasks, TPL lets you run them in parallel and thus allows you to benefit from modern multicore CPUs.

Favor asynchrony over synchrony where operations are network-bound or I/O-bound instead of CPU-bound. Database operations, web service calls, and extensive file system operations are prime candidates for asynchrony.

In general, these situations are applicable for all server applications, but the choice of asynchronous or synchronous programming depends on the application itself. It should be tested to determine whether asynchrony provides a performance benefit or not.

Old Asynchronous Programming Models in .NET

An asynchronous programming paradigm is something that the .NET Framework has provided support for since version 1.1, and the .NET Framework has introduced three different types of asynchronous programming models since then. With .NET Framework 1.1, the Asynchronous Programming Model (APM) was introduced. In version 2.0, the .NET Framework provided Evented Asynchronous Programming (EAP). The most important step ahead in APM terms was the introduction of Task-based Asynchronous Pattern (TAP), along with Task Parallel Library (TPL), in .NET Framework 4.0. Each new programming model was slightly better than the previous one, but still not the most desirable programming experience.

This book’s main concern will be Task-based Asynchronous Pattern, along with new C# 5.0 asynchronous language features, but in this section, we’ll start by going through each model. We’ll see how we can use asynchrony with these patterns and how the patterns differ from each other.

Asynchronous Programming Model (APM)

In Asynchronous Programming Model (APM), asynchronous operations are represented by a pair of Begin/End methods such as FileStream.BeginRead and FileStream.EndRead.

The .NET Framework has various types of Begin/End methods designed to perform asynchronous operations. For example, System.IO.FileStream class has Read, BeginRead, and EndRead methods. Read method reads the number of bytes from a file stream synchronously and blocks the thread it runs on till the operation is completed. However, BeginRead and EndRead method pairs enable execution of the same operation asynchronously in a very complex manner.

When the BeginRead method is called, the asynchronous operation is started, and the method returns immediately while the operation is still running. When the operation is completed, the CLR gets notified by the Windows kernel about the status of the operation, and then the delegate that’s been specified as a parameter to BeginRead method gets invoked. Inside this delegate, the EndRead method needs to be executed by passing the IAsyncResult object (which has been passed to the delegate by the BeginRead method) as a parameter in order to access the bytes read from the file stream.

Listing 2-1 shows the code that performs the action just explained.

Listing 2-1.  Reading a File from the File System Asynchronously with APM Methods

static byte[] buffer = new byte[100];

static void Main(string[] args) {

    const string filePath = @"C:AppsFoo.txt";

    FileStream fileStream = new FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read, 1024,
        FileOptions.Asynchronous);

    IAsyncResult result = fileStream.BeginRead(buffer, 0, buffer.Length,
            new AsyncCallback(CompleteRead), fileStream);

    Console.ReadLine();
}

static void CompleteRead(IAsyncResult result) {

    Console.WriteLine("Read Completed");

    FileStream strm = (FileStream)result.AsyncState;

    // Finished, so we can call EndRead and it will return without blocking
    int numBytes = strm.EndRead(result);

    strm.Close();

    Console.WriteLine("Read {0} Bytes", numBytes);
    Console.WriteLine(BitConverter.ToString(buffer));
}

This code is only for one operation. Imagine the complexity if nested asynchronous operations had to be performed! Also, figuring out how to perform exception handling isn’t as obvious as it might be. The next model looks slightly better than this one, but it, too, has its cons.

Event-based Asynchronous Pattern (EAP)

In Event-based Asynchronous Pattern (EAP), asynchronous operations are represented by a method/event pair named OperationNameAsync and OperationNameCompleted; for example, WebClient.DownloadStringAsync and WebClient.DownloadStringCompleted. In the .NET Framework, some types support EAP as well. For example, the System.Net.WebClient class defines the DownloadStringAsync method and a DownloadStringCompleted event, to be called by the DownloadStringAsync method when the downloading operation is completed. DownloadStringAsync method also passes the argument e, a type of System.Net.DownloadStringCompletedEventArgs that contains the results of the operation  and additional information about it.

USING THE SYSTEM.NET.WEBCLIENT ASYNCHRONOUS METHODS

In this section, we use the System.Net.WebClient class and its different types of asynchronous operations to demonstrate the usage of old asynchronous patterns. However, WebClient uses HttpWebRequest, which does a lot of setup work synchronously (proxy, DNS, connection pooling, etc.) and unfortunately is not therefore very asynchronous, even if “asynchronous” methods are used. This issue is not present with the HttpClient API, which is introduced in .NET 4.5. We don’t therefore recommend using WebClient for HTTP request calls. In Chapter 4, we’ll show how to use HttpClient asynchronous methods.

Listing 2-2 shows how to download a web page as string with WebClient EAP methods and events.

Listing 2-2.  Downloading a Web Page as String with WebClient EAP Methods and Events

static void Main(string[] args) {

    WebClient client = new WebClient();

    client.DownloadStringCompleted += (sender, eventArgs) => {

        Console.WriteLine(eventArgs.Result);
    };

    client.DownloadStringAsync(new Uri("http://example.com"));

    Console.ReadLine();
}

As this pattern is also not the desired pattern for asynchronous programming in .NET, let’s look at Task-based Asynchronous Pattern (TAP), which we’ll be working with throughout the rest of this book.

Task-Based Asynchronous Pattern (TAP)

TAP is a pattern for asynchrony in the .NET Framework. It is based on the Task and Task<TResult> types in the System.Threading.Tasks namespace, which are used to represent arbitrary asynchronous operations introduced inside the Task Parallel Library (TPL). TPL is a set of public types and APIs in the System.Threading and System.Threading.Tasks namespaces in the .NET Framework.

In TAP, the asynchronous methods do not represent the real result as a returning object. Instead, they represent an ongoing task which will only eventually be completed at some later point. Understand from this expression that the asynchronous methods may be returned before the operation is completed. Those methods may, in addition, complete synchronously.

The System.Threading.Tasks.Task class is the one that represents asynchronous operations in TAP. An asynchronous method can return either Task or Task<T> for some T, based on whether the corresponding synchronous method would return void or a type T. If the method returns Task, there is not going to be an end result for the operation—except for the operation status that the Task class instance will carry. If the returning result of the asynchronous method is Task<T> for some T, the result will also have the end result (which is of type T) along with the Task class instance; it can be reached through the Result property of the Task class instance. Listing 2-3 shows how to retrieve the result of an asynchronous method directly from the Result property of the Task class.

Listing 2-3.  Retrieving the Result from an Asynchronous Method Directly from the Result Property of the Task Instance

static void Main(string[] args) {

    var result = DoWorkAsync().Result;
    Console.WriteLine(result);
    Console.ReadLine();
}

static Task<string> DoWorkAsync() {

    return Task<string>.Factory.StartNew(() => {
        Thread.Sleep(3000);
        return "Hello world...";
    });
}

Inside the sample, we used the StartNew method of the TaskFactory class through Task.Factory to create and start a Task. This is not the optimal way of doing what needs to be done here (this topic is covered in a later subsection, “TaskCompletionSource<TResult>”). The StartNew method accepts a delegate to schedule the Task for execution. Inside the lambda function we are passing as a delegate, we wait for three seconds and then return the result.

image Tip  Asynchronous methods in TAP are usually named with an “Async” suffix attached to the operation’s name; for example, MethodNameAsync. If adding a TAP method to a class that already contains a method MethodNameAsync, the suffix “TaskAsync” may be used instead, resulting in MethodNameTaskAsync.

However, invoking an asynchronous method, as shown in Listing 2-3, is not good practice with methods whose return type is Task<T>. Trying to get the result directly from the Result property of the Task class instance will block the thread until the operation is completed. That makes no sense for what we are trying achieve here. The next subsection shows how to address this problem.

image Note  A bit later in this chapter, you will see that most of the work we are trying to do is actually done by the new C# 5.0 asynchronous language features, but it is useful to learn a few of TAP’s important points before diving into async/await features.

Continuations, Errors, and Task Status

In addition to retrieving the result out of a Task object through its Result property, we can work with the ContinueWith method of the Task class. The ContinueWith method creates a continuation that executes when the target Task completes. Listing 2-4 shows how to provide continuations to a task that represents an ongoing operation.

Listing 2-4.  Retrieving a Result from an Asynchronous Method Inside the ContinueWith Method of a Task Instance

static void Main(string[] args) {

    DoWorkAsync().ContinueWith(task => {

        Console.WriteLine(task.Result);
    });
    Console.ReadLine();
}

static Task<string> DoWorkAsync() {

    return Task<string>.Factory.StartNew(() => {
        Thread.Sleep(3000);
        return "Hello world...";
    });
}

Listing 2-4 still retrieves the result through the Result property, but this time the operation is sure to have been completed. So reaching out to the Result property gets an immediate return because the result is already available. Although this way of retrieving results won’t block the main thread, it’s still not the optimal way of working with asynchronous methods in TAP.

Calling the ContinueWith method this way without checking the status of the operation will cause the continuation to run in every case, regardless of the task’s final state. To handle this situation, use one of the overloads of the ContinueWith method that accepts a parameter of the type System.Threading.Tasks.TaskContinuationOptions. Passing a TaskContinuationOptions instance as a parameter to the ContinueWith method let you specify where to run the delegate function. Listing 2-5 shows how to set TaskContinuationOptions.

Listing 2-5.  Using TaskContinuationOptions to Specify Where to Run the delegate Function

static void Main(string[] args) {

    DoWorkAsync().ContinueWith(task => {

        Console.WriteLine(task.Result);
    }, TaskContinuationOptions.NotOnFaulted);

    DoWorkAsync().ContinueWith(task => {
        
        Console.WriteLine(task.Exception.InnerException.Message);
    }, TaskContinuationOptions.OnlyOnFaulted);

    Console.ReadLine();
}

static Task<string> DoWorkAsync() {

    return Task<string>.Factory.StartNew(() => {
        Thread.Sleep(3000);
        return "Hello world...";
    });
}

On the other hand, there might be some asynchronous methods which complete synchronously. So check the status of the calls to see whether they completed asynchronously or not. Based on  the result, unnecessary calls to ContinueWith methods can be avoided. Listing 2-6 shows an example of such use.

Listing 2-6.  Detecting Whether the Task Has Completed Synchronously

static void Main(string[] args) {

    var doWorkTask = DoWorkAsync();

    if (doWorkTask.IsCompleted) {

        Console.WriteLine(doWorkTask.Result);

    } else {

        doWorkTask.ContinueWith(task => {

            Console.WriteLine(task.Result);
        }, TaskContinuationOptions.NotOnFaulted);

        doWorkTask.ContinueWith(task => {

            Console.WriteLine(task.Exception.InnerException.Message);
        }, TaskContinuationOptions.OnlyOnFaulted);

         Console.ReadLine();
    }
}

static Task<string> DoWorkAsync() {

    return Task<string>.Factory.StartNew(() => {
        Thread.Sleep(3000);
        return "Hello world...";
    });
}

In the sample shown in Listing 2-6, we check whether the task is completed. If it is, then we draw the result directly from the Result property of the Task class instance. Doing so is safe in this case because we know the task is completed and we won’t block any thread.

Finally, we have a chance to see the status of the Task inside the continuation provided. The Task class holds a property, Status, which is a type of the TaskStatus enumeration. As soon as a Task object is initiated, the Status property is set to a value and the value of the Status property changes throughout its life cycle. A Task object can be in any one of the seven different states listed in Table 2-1.

Table 2-1. The TaskStatus Enumeration Values and Their Descriptions

TaskStatus Enumeration Value Description
Created The starting state of the Task object created through its constructor.
WaitingForActivation The starting state of a Task object created through such methods as ContinueWith, ContinueWhenAll, ContinueWhenAny, and FromAsync, as well as from a TaskCompletionSource<TResult>.
WaitingToRun This state has been set when the Task object is scheduled for execution but has not yet begun executing.
Running This state indicates that the Task is running but is not yet completed.
WaitingForChildrenToComplete This state indicates that the Task has finished executing and is implicitly waiting for attached child tasks to complete.
RanToCompletion One of the three final states. It indicates that the Task has completed its execution successfully.
Canceled One of the three final states. It indicates that the Task object in this state has completed its execution but has been canceled.
Faulted One of the three final states. It indicates that the Task has been completed due to an unhandled exception.

The next section shows how to determine the status of a Task through a TaskStatus enumeration object.

Composition

It’s sometimes helpful to execute multiple asynchronous operations in concert to perform more complex operations. Compared with previous asynchronous patterns in the .NET Framework, combining asynchronous operations with TPL is fairly straightforward. Still, it requires effort, and there are various ways of achieving it.

The Task class has two static methods: WaitAll and WaitAny. The WaitAll method accepts a collection of tasks and waits for all of them to complete. WaitAny, on the other hand, accepts a collection of tasks but waits until at least one of them is completed. You can start a bunch of related asynchronous operations you would like to execute in combination and then pass them all to WaitAll or WaitAny. Then you can collect the results once all are complete. Since these two methods will block the current thread, it is not the best way to achieve what you are after.

On the other hand, the TaskFactory class offers the ContinueWhenAll method, which invokes a continuation only once all of the asynchronous operations are completed. There is another method, ContinueWhenAny, and it invokes the continuation as soon as at least one task is completed. Neither of these two methods block the current thread, and they invoke the continuation by passing the completed task or tasks if the method in question is ContinueWhenAny. Listing 2-7 shows how the ContinueWhenAll method can be used.

Listing 2-7.  An Example of TaskFactory.ContinueWhenAll in Use

static void Main(string[] args) {

    //First async operation
    var httpClient = new HttpClient();
    Task<string> twitterTask =
        httpClient.GetStringAsync("http://twitter.com");

    //Second async operation
    var httpClient2 = new HttpClient();
    Task<string> googleTask =
        httpClient2.GetStringAsync("http://www.google.com");

    Task.Factory.ContinueWhenAll(new[] { twitterTask, googleTask }, (tasks) => {

        //all of the tasks have been completed.
        //Reaching out to the Result property will not block.

        foreach (var task in tasks) {

            if (task.Status == TaskStatus.RanToCompletion) {

                Console.WriteLine(task.Result.Substring(0, 100));
            }
            else if (task.Status == TaskStatus.Canceled) {

                Console.WriteLine("The task has been canceled. ID: {0}", task.Id);
            }
            else {
                Console.WriteLine("An error has been occurred. Details:");
                Console.WriteLine(task.Exception.InnerException.Message);
            }
        }
    });

    Console.ReadLine();
}

To demonstrate how our example works, we used the new HttpClient API, which is introduced with .NET 4.5 to download web pages of two different web sites. We kicked off the operations and passed the two of them as parameters into the Task.Factory.ContinueWhenAll method. We also provided a continuation, which will be invoked when all these two tasks are completed. Inside the continuation, we have all the completed tasks in our hands. The first thing we do is put them inside a foreach loop. Notice that we are checking the status of each completed task before doing anything else (cf. the previous subsection, where the task statuses were explained). When the application is run, we see the first 100 characters of each web page’s source code on the console if each web call completes successfully (see Figure 2-6).

9781430247258_Fig02-06.jpg

Figure 2-6. The console window showing the result of our ContinueWhenAll example

Although the ContinueWhenAll helper method allows the asynchronous operations to be run together, it actually doesn’t compose them together. At the end of the operations, you’ll have a reference for each completed task. In order to actually compose them together, you’ll need to work with Task.WhenAll. This static class was introduced only in .NET 4.5 and was therefore not available in prior versions of .NET. There is another static method, WhenAny, which is equivalent to ContinueWhenAny.

Unlike TaskFactory.ContinueWhenAll, Task.WhenAll returns a Task or Task<T[]> that represents the completion of all of the supplied tasks. Listing 2-8 shows an approach for our little sample different from the one in Listing 2-7.

Listing 2-8.  Another Example of Task.WhenAll in Use

static void Main(string[] args) {

    //First async operation
    var httpClient = new HttpClient();
    Task<string> twitterTask =
        httpClient.GetStringAsync("http://twitter.com");

    //Second async operation
    var httpClient2 = new HttpClient();
    Task<string> googleTask =
        httpClient2.GetStringAsync("http://google.com");

    Task<string[]> task = Task.WhenAll(twitterTask, googleTask);

    task.ContinueWith(stringArray => {

        //all of the tasks have been completed.
        //Reaching out to the Result property will not block.

        if (task.Status == TaskStatus.RanToCompletion) {

            for (int i = 0; i < stringArray.Result.Length; i++) {

                Console.WriteLine(stringArray.Result[i].Substring(0, 100));
            }
        }
        else if (task.Status == TaskStatus.Canceled) {

            Console.WriteLine("The task has been canceled. ID: {0}", task.Id);
        }
        else {
            Console.WriteLine("An error has been occurred. Details:");
            foreach (var ex in task.Exception.InnerExceptions) {
                Console.WriteLine(ex.Message);
            }
        }
    });

    Console.ReadLine();
}

In the case here, when the two tasks are passed into Task.WhenAll, we know that we will get back a Task<string[]>, representing an ongoing operation. So we provide a continuation to allow processing to go on when all tasks are completed. Look inside the continuation delegate; you will see that the implementation is nearly the same as that shown in Listing 2-7. However, we are looping through the InnerExceptions property of the AggregateException instance to retrieve the exceptions when the task state is known to be faulted—that is, when at least one exception is thrown—because Task.WhenAll performs multiple ongoing operations and returns only one Task object. So if there are multiple errors, we want to reach all of them, not just one.

It is also very important to know how the returned Task or Task<T[]> object’s status is set.

  • If any of the supplied tasks completes in a Faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
  • If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
  • If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
  • If the supplied array and/or enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion state before it’s returned to the caller.

As you’ve seen, the composition of multiple asynchronous operations is fairly straightforward with TPL. However, there is still a lot of work to do inside the continuation to figure out the returned operation’s completion state. Inside the “C# 5.0 Asynchronous Language Features” section in this chapter, these issues are addressed.

TaskCompletionSource<TResult>

You’ve probably noticed that in an ongoing operation, Task.Factory.StartNew has been used to create new Task objects. As you’ve seen, StartNew accepts a delegate to schedule the Task for execution. On the other hand, no work’s been done so far that requires a thread switch inside the delegate provided. However, Task.Factory.StartNew runs our code in another thread, and as you know, thread switching has its own cost. Besides, using Task.Factory.StartNew to represent an ongoing operation runs counter to the title of this chapter, because Task.Factory.StartNew doesn’t produce an asynchronous operation; instead, it introduces multithreading. Even if an asynchronous code is run inside the delegate passed into Task.Factory.StartNew, we’ll still be doing multithreading. That’s not a good idea for a server application in most scenarios.

In the ASP.NET Web API framework, you’ll see that there are lots of extensibility points which require a return to a Task or Task<T> for some T. You won’t always need a new thread for our code to run because our code might not have any risk of blocking the current thread or producing an asynchronous operation. Having said all of this, we shouldn’t be using Task.Factory.StartNew to create new Task objects if code doesn’t need to run in another thread. Instead, we should work with the System.Threading.Tasks.TaskCompletionSource generic class to return new Task objects.

TaskCompletionSource<TResult> is not just about the scenario just explained. One of TPL’s primary developers, Stephen Toub, has defined TaskCompletionSource as follows:

The TaskCompletionSource<TResult> type serves two related purposes, both alluded to by its name: it is a source for creating a task, and the source for that task’s completion. In essence, a TaskCompletionSource<TResult> acts as the producer for a Task<TResult> and its completion.

http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx

The TaskCompletionSource<TResult> object provides methods for controlling the lifetime and completion of the associated Task, such as SetResult, SetCanceled, and SetException methods. Those methods have their TrySet* variants as well, and they return Booleans as a result. TrySet* variants of those methods exist because a Task can be completed only once. So trying to complete a Task multiple times with TrySet* variants of those methods will return false.

To illustrate how TaskCompletionSource is used, Listing 2-9 creates a very naive example.

Listing 2-9.  An Example of TaskCompletionSource in Use

public class AsyncFactory {

    public static Task<int> GetIntAsync() {
            
        var tcs = new TaskCompletionSource<int>();

        var timer = new System.Timers.Timer(2000);
        timer.AutoReset = false;
        timer.Elapsed += (s, e) => {
            tcs.SetResult(10);
            timer.Dispose();
        };

        timer.Start();
        return tcs.Task;
    }
}

What is here is an internally bogus asynchronous method; it fakes an operation that takes two seconds as a parameter and enables the client to consume this method in an unblocking fashion. We have created a TaskCompletionSource<int> instance inside the method, as well as a Timer object with a two-second interval, but the delegate is run just once and then the Timer object is disposed of.

For consuming this static method, the implementation won’t be any different than it is for the others (see Listing 2-10).

Listing 2-10.  Consuming Our AsyncFactory.GetIntAsync Method

static void Main(string[] args) {

    AsyncFactory.GetIntAsync().ContinueWith((task) => {

        if (task.Status == TaskStatus.RanToCompletion) {

            Console.WriteLine(task.Result);
        }
        else if (task.Status == TaskStatus.Canceled) {

            Console.WriteLine("The task has been canceled.");
        }
        else {

            Console.WriteLine("An error has been occurred. Details:");
            Console.WriteLine(task.Exception.InnerException.Message);
        }
    });

    Console.ReadLine();
}

Though it’s a matter not entirely related to TaskCompletionSource, it is worth mentioning here that with .NET 4.5, there is a new static method, FromResult, that takes a T object and returns a Task<T> that is already completed without consuming any other threads. This returned Task<T> has the RanToCompletion status set. Listing 2-11 shows a simple example for use of the FromResult method.

Listing 2-11.  An Example of the Task.FromResult Method in Use

static void Main(string[] args) {
            
    var intTask = GetIntAsync();

    if (intTask.IsCompleted) {

        Console.WriteLine("Completed Instantly: {0}", intTask.Result);
    }
    else {

        intTask.ContinueWith((task) => {

            if (task.Status == TaskStatus.RanToCompletion) {

                Console.WriteLine(task.Result);
            }
            else if (task.Status == TaskStatus.Canceled) {

                Console.WriteLine("The task has been canceled.");
            }
            else {
                Console.WriteLine("An error has been occurred. Details:");
                Console.WriteLine(task.Exception.InnerException.Message);
            }
        });
    }

    Console.ReadLine();
}

static Task<int> GetIntAsync() {

    return Task.FromResult(10);
}

Without having to employ the TaskCompletionSource object, we were able to create a new completed Task<int> object. We also didn’t introduce a thread switch. The Task.FromResult method is one of those we’ll use a lot in samples throughout this book.

Cancellation

In an asynchronous operation, you sometimes need to control its state to cancel the operation. With .NET 4.0, a new model for cancellation of asynchronous or long-running synchronous operations has been introduced. This model is based on an object called cancellation token.

The object that invokes the cancelable operation passes the cancellation token to the operation. At some point, the object that created the token can signal it to request a cancellation to get the operation to stop processing. The object receiving the token can then pass it to other operations; in this way it supports the cancellation chain. However, only the issuer of the cancellation token can issue the cancellation request, and the request can be done only once. On the other hand, there’s no way to get the requested cancellation back. The cancellation, so to speak, can’t be canceled.

This cancellation model is embraced by the .NET Framework, and as you will see, the ASP.NET Web API framework leverages the same model. There are three types of objects that you need to be aware for this cancellation model (see Table 2-1):

  • CancellationTokenSource: The object that creates a cancellation token and manages its state, such as by issuing the cancellation request for all copies of that token.
  • CancellationToken: A value type passed to one or more listeners, who then can observe this object to figure out whether the cancellation is requested or not by polling, callback, or wait handle.
  • OperationCanceledException: Listeners have the option to throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

Listing 2-9 shows a bogus asynchronous method over whose completion we have no control. The operation may complete instantly, or it may take ten minutes. However, nothing prevents the implementation of such functionality. Listing 2-12 shows a sample implementation for it.

Listing 2-12.  An Example of a Cancellation Token in Use

public class AsyncFactory {

    public static Task<int> GetIntAsync(
        CancellationToken token = default(CancellationToken)) {

        var tcs = new TaskCompletionSource<int>();

        if (token.IsCancellationRequested) {
            tcs.SetCanceled();
            return tcs.Task;
        }

        var timer = new System.Timers.Timer(2000);
        timer.AutoReset = false;
        timer.Elapsed += (s, e) => {
            tcs.TrySetResult(10);
            timer.Dispose();
        };
            
        if (token.CanBeCanceled) {
                
            token.Register(() => {
                tcs.TrySetCanceled();
                timer.Dispose();
            });
        }

        timer.Start();
        return tcs.Task;
    }
}

For the GetIntAsync method, a CancellationToken value is now accepted as a parameter, but passing a CancellationToken isn’t mandatory. The first thing to do is check whether the cancellation is requested. This information is found through the IsCancellationRequested property of the CancellationToken. If the cancellation is requested, then set the status of the Task object to Cancelled and return from there immediately. If the condition is false, continue to implement the logic in Listing 2-9. However, there is another piece of code there, one that handles the actual cancellation logic.

First of all, we check whether the token can be canceled at all. For example, if the caller of this method doesn’t provide a cancellation token, the token will be set to its default value, and there is then no way for it to be canceled. If the token is cancelable, we use the Register method of the token to register a callback when the cancellation request is signaled. Also, notice that the Try* variants of the set methods have been used for TaskCompletionSource because when the timer has elapsed, the cancellation can be requested at the same time. So it isn’t known for sure which one has set the status.

Let’s see how to call this method by providing a cancellation token (see Listing 2-13).

Listing 2-13.  Consuming GetIntAsync by Providing a Cancellation Token

static void Main(string[] args) {

    CancellationTokenSource cts = new CancellationTokenSource();
    cts.CancelAfter(1000);

    AsyncFactory.GetIntAsync(cts.Token).ContinueWith((task) => {

        //We get the response.
        //So dispose the CancellationTokenSource
        //so that it is not going to signal.
        cts.Dispose();

        if (task.Status == TaskStatus.RanToCompletion) {

            Console.WriteLine(task.Result);
        }
        else if (task.Status == TaskStatus.Canceled) {

            Console.WriteLine(
                "The task has been canceled.");
        }
        else {
            Console.WriteLine(
                "An error has been occurred. Details:");
            Console.WriteLine(
                task.Exception.InnerException.Message);
        }
    });

    Console.ReadLine();
}

First of all, a CancellationTokenSource object has been created to handle the cancellation token. Then, the CancelAfter method of the CancellationTokenSource has been used to tell it to request a cancellation after one second has elapsed. Finally, the Token property of the CancellationTokenSource object has been used to retrieve a CancellationToken value and has fired up the GetIntAsync method by passing that token. As the CancellationTokenSource is unaware of the state of our ongoing task, dispose of the CancellationTokenSource object as soon as our continuation method is fired, because it will still try to cancel the request even if the task is completed in less than one second. The rest of the method is same as before. When this application is run, you should be able to see that the cancellation occurs (see Figure 2-7).

9781430247258_Fig02-07.jpg

Figure 2-7. Console window showing the result of the canceled operation

Linked Cancellation Tokens

Some scenarios require listening to multiple cancellation tokens at once. For example, a cancelable object may accept a cancellation token as a parameter and also have an internal cancellation token. In order to handle such a scenario, create a so-called linked token, one that can join two or more tokens into one.

The CancellationTokenSource class provides a static method named CreateLinkedTokenSource to create a new CancellationTokenSource instance, which consists of the provided cancellation tokens. You can pass as many cancellation tokens as you like into CreateLinkedTokenSource method. The created CancellationTokenSource will signal the cancellation as soon as any one of the individually provided cancellation tokens is canceled.

Listing 2-14 provides an example.

Listing 2-14.  InternalGetIntAsync Method and Linked Cancellation Token Sample

class Program {

    static void Main(string[] args) {

        CancellationTokenSource cts =
            new CancellationTokenSource();
        cts.CancelAfter(1000);

        Stopwatch watch = new Stopwatch();
        watch.Start();

        InternalGetIntAsync (cts.Token).ContinueWith((task) => {

            Console.WriteLine(
                "Elapsed time: {0}ms",
                watch.Elapsed.TotalMilliseconds);
            watch.Stop();

            //We get the response.
            //Dispose of the CancellationTokenSource
            //so that it is not going to signal.
            cts.Dispose();

            if (task.Status == TaskStatus.RanToCompletion) {

                Console.WriteLine(task.Result);
            }
            else if (task.Status == TaskStatus.Canceled) {

                Console.WriteLine("The task has been canceled.");
            }
            else {
                Console.WriteLine(
                    "An error has been occurred. Details:");
                Console.WriteLine(
                    task.Exception.InnerException.Message);
            }
        });

        Console.ReadLine();
    }

    static Task<int> InternalGetIntAsync(
        CancellationToken token) {

        var cts = new CancellationTokenSource(500);
        var linkedTokenSource =
            CancellationTokenSource.CreateLinkedTokenSource(
                cts.Token, token);

        return AsyncFactory.GetIntAsync(linkedTokenSource.Token);
    }
}

This example still uses the implementation of the AsyncFactory.GetIntAsync method used in Listing 2-12, but this time a proxy method is in between. This proxy method, the InternalGetIntAsync method, has its own internal cancellation token and also accepts another cancellation token as a parameter. The two tokens can be combined into one through the CreateLinkedTokenSource method, and the generated token can be passed into the AsyncFactory.GetIntAsync method.

Now this new token source signals the cancellation after 500 milliseconds, and our cancellation token requests a cancellation after a second. Also, since AsyncFactory.GetIntAsync takes two seconds to complete the request, this operation should be canceled after 500 milliseconds. For this sample, unlike the others, we also use a Stopwatch to see how long completing the operation takes. As you will see, it will take approximately half a second; then the status of the completed task will be canceled (see Figure 2-8).

9781430247258_Fig02-08.jpg

Figure 2-8. The console window showing the result of the canceled operation and its completion time

C# 5.0 Asynchronous Language Features

C# 5.0 has two new keywords: async and await. They provide a whole new asynchronous programming experience, one that .NET stack hasn’t had so far. These features, also known as Asynchronous Functions, move most of the work from developer to compiler.

Asynchronous Functions in C# 5.0 constitute a new feature and provide a much easier way of dealing with asynchronous operations. Inside asynchronous functions, await expressions await ongoing tasks and suspend the logical execution flow of the code. This capacity will cause the rest of the function to be transparently signed up as a continuation of the awaited task and allows you to write code as though it was synchronous, even though the compiler turns it into an asynchronous function. As a result, asynchronous code retains its logical structure from the developer’s perspective, while the code structure is dramatically changed by the compiler. With these new asynchronous language features, the compiler is not just generating the code that we would have written on our own, but it also does it in a virtually optimum way. Going into detail on how this is enabled is beyond this book’s scope. Here, what we really want to achieve is to familiarize ourselves with these new features.

ASYNC/AWAIT IN .NET V4.0

We are targeting .NET 4.5 in order to leverage C# 5.0’s asynchronous language features—new features that are not compatible with .NET 4.0. If you would like to work with the async and await keywords in .NET 4.0, you’ll need additional pieces, depending on the Visual Studio version you use.

If you are working with Visual Studio 2010, you’ll need to install Visual Studio Async CTP, which will change the C# and Visual Basic compilers, and install necessary libraries and samples. If the installation completes successfully, you’ll find the necessary libraries in the %userprofile%DocumentsMicrosoft Visual Studio Async CTPSamples directory. For ASP.NET Web API, you’ll need to add AsyncCtpLibrary.dll as a reference to your project. However, keep in mind that this solution works less optimally than .NET 4.5 and C# 5.0 compilers, nor do we know how long it is going to be supported.

If you are using Visual Studio 2012 and would like to work with async/await keywords in .NET Framework 4.0, you can install the Microsoft.Bcl.Async NuGet package. By the time of this writing, the Microsoft.Bcl.Async NuGet package is a pre-release NuGet package. So, you need to add the -pre switch to install this package through the NuGet Package Manager Console inside Visual Studio. You can find more information about this package from the BCL team’s blog post: http://blogs.msdn.com/b/bclteam/archive/2012/10/22/using-async-await-without-net-framework-4-5.aspx.

In both scenarios, the compilers will work with these two keywords. On the other hand, there will be an additional class, TaskEx, which will hold WhenAll, WhenAny, and some other utility methods. So you’ll need to modify the code explained here accordingly. All code other than these utility methods will work as in .NET 4.5.

Previous sections have shown how to use the DownloadStringTaskAsync method in WebClient API. Listing 2-15 shows the same operation with the new language features.

Listing 2-15.  Using WebClient with async and await Keywords

public async Task<string> DowloadPage(string uri) {

    using (WebClient client = new WebClient()) {

        string htmlString = await client.DownloadStringTaskAsync(uri);
        return htmlString;
    }
}

There are a few specific things about this method to notice. First of all, the method is marked with the async modifier. Without this modifier, you’ll get a compile-time error if you try to work with await expressions inside the method since the await operator can be used only within an async method. The second thing to note is the return type of this method. It is indicated that the method will return Task<string>, but inside the method the object returned is a type of string. This is perfectly valid because the await expression takes care of this situation, and the compiler generates the necessary code to make this work. If an asynchronous function’s return type is either void or Task, the return statement must not have an expression. If the return type of the asynchronous function is Task<T> for some T, the return statement must have an expression implicitly convertible to T. Also notice that we were able to use the await keyword inside the using statement. At first glance, it might seem that the WebClient instance will be disposed before it completes it work, but the reality is that the compiler will also inspect this and ensure that the WebClient instance is disposed at the right time.

Chaining multiple asynchronous methods with the await pattern is extremely easier and cleaner compared with a pure TAP model. Listing 2-16 shows this in action.

Listing 2-16.  Multiple Asynchronous Operation Chaining Cample

private async void Button_Click_1(object sender, RoutedEventArgs e) {

    using (Stream stream = await GetWebPageAsync())
    using (var fileStream = new FileStream(@"c:appsmsg.bin", FileMode.Create)) {
        await stream.CopyToAsync(fileStream);
        StatusLabel.Content = "Done...";
    }
}

private async Task<Stream> GetWebPageAsync() {

    using (var httpClient = new HttpClient()) {
        var stream = await httpClient.GetStreamAsync("http://www.apress.com");
        return stream;
    }
}

This little WPF application downloads the Apress home page and saves it to the disk. The GetWebPageAsync method downloads the web page and returns it as a stream but does it asynchronously. Again, we used new HttpClient here to download the web page asynchronously. Also, notice that we make the asynchronous call inside the using block, and we didn’t hesitate to use it because, as mentioned before, the compiler does all the heavy lifting here and makes sure that HttpClient won’t be disposed of until the operation is completed.

There is also an event handler named Button_Click_1 for the button. The async and await keywords can be used inside a method whose return type is void, but it is better to avoid that as much as possible and return at least Task, because the caller of the asynchronous method probably wants to know the details and status of the operation even if the method isn’t returning anything. However, as this is an event handler, there’s no other choice here. Inside the Button_Click_1 method, you can consume the GetWebPageAsync method and await it as well. As soon as you get the Stream from the GetWebPageAsync method, go ahead and save it to the disk. This is done using the CopyToAsync method of the Stream class. The CopyToAsync method returns Task, and therefore you can await that as well. After every operation is completed, the message on the UI indicating that the operation is completed is displayed.

When the application is fired up and the button pressed, you won’t lose any UI responsiveness because every operation is performed asynchronously (see Figure 2-9).

9781430247258_Fig02-09.jpg

Figure 2-9. Our application UI

In the .NET Framework, the recommended pattern for asynchrony is TAP, and the Task class has the main role in this pattern. When you call a method that returns Task or Task<T> for some T and “await” that, the compiler does the heavy lifting: assigning continuations, handling exceptions, and so on. As we mentioned and saw in action, Task or Task<T> for some T can be awaited by the await keyword to suspend the execution of an asynchronous function. Besides, any type that satisfies the await pattern can be awaited. This chapter won’t go into the details of this pattern since its main concern is to provide the infrastructure to work with asynchrony.

On the other hand, we will go through some of the important concepts of this new pattern to make the most out of it when working in an ASP.NET Web API application.

Exception Handling

Listing 2-16’s code didn’t perform any exception handling, but it’s possible that an operation may crash for any of several reasons. Exception handling is not an easy task in general, and it is certainly not easy when dealing with asynchrony. However, when using async and await keywords, exception handling is easier than with the pure TAP implementation. As we usually do when writing synchronous code, we need to put our awaited method inside a try/catch block. The compiler then understands our intention and generates the code that will enable us to catch the exception.

Also, as was seen in earlier sections, the status of the Task is set to Faulted, and the actual exception is wrapped inside the AggregateException if an exception occurs during an asynchronous operation. The main reason for this is that it was not possible to rethrow exceptions without losing the actual stack trace, which is vitally important for examining the exception. However, with .NET v4.5, it’s now possible to rethrow the exception without losing any actual stack trace information.

Listing 2-17 shows a modified version of the code inside Listing 2-16.

Listing 2-17.  Multiple Asynchronous Operation Chaining Sample with Exception Handling

private async void Button_Click_1(object sender, RoutedEventArgs e) {

    try {

        using (Stream stream = await GetWebPageAsync())
        using (var fileStream = new FileStream(@"c:appsmsg.bin", FileMode.Create)) {
            await stream.CopyToAsync(fileStream);
            StatusLabel.Content = "Done...";
        }
    }
    catch (Exception ex) {

        StatusLabel.Content = string.Format(
            "An exception occurred: {0}{1}Stack Trace:{1}{2}",
            ex.Message,
            Environment.NewLine,
            ex.StackTrace
        );
        StatusLabel.Foreground = Brushes.Red;
    }
}

private async Task<Stream> GetWebPageAsync() {
            
    using (var httpClient = new HttpClient()) {
        var stream = await httpClient.GetStreamAsync("http://localhost:9999");
        return stream;
    }
}

We’ve exchanged the URI for a broken URI to get an exception to demonstrate our example. When the application is run and the button is pressed, the exception details should be displayed on the UI (see Figure 2-10).

9781430247258_Fig02-10.jpg

Figure 2-10. Our application UI displaying the exception details

The exception handling with async and/or await is an extensive topic as well. As it’s outside this book’s scope, it’s not looked at in detail.

SynchronizationContext

There’s another important feature of the async and await keywords: support for SynchronizationContext. When you await an asynchronous method, the compiler hooks up the continuation, if there is any, and the resulting code is context-aware. Thus, if a SynchronizationContext is available, the await expression will capture it and use it to invoke the continuation. This is why we were able to work with the UI controls directly right after the await expressions, as a SynchronizationContext is available in WPF applications.

The SynchronizationContext is not just about the TPL, and it’s been inside the framework since .NET 2.0. If you are working with the async and await keywords, however, all the code that you would be writing to work with the SynchronizationContext will already be written for you by the compiler. Your code will always execute along with the right context.

However, unless enough care is taken, this feature may lead to some problems. If you consume a method in a blocking fashion and that method has been implemented by using the new asynchronous language features, you will end up with a deadlock if you have an available SynchronizationContext. When, for instance, you wait on the Task with the Wait method or take the result directly from the Result property of the Task, you block the main thread at the same time. When eventually the Task completes inside that method in the thread pool, it is going to invoke the continuation to post back to the main thread (as we never left it), because SynchronizationContext.Current is available and captured. But there’s a problem: the UI (or main) thread is blocked. You have a deadlock!

Having created a simple .NET client for an HTTP API (Listing 2-18), we consume the HTTP API by using the HttpClient’s asynchronous methods along with the async/await keywords.

Listing 2-18.  Simple HTTP API .NET Client

public class Car {

    public int Id { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public float Price { get; set; }
}

public class SampleAPIClient {

    private const string ApiUri = "http://localhost:17257/api/cars";

    public async Task<IEnumerable<Car>> GetCarsAsync() {

        using (HttpClient client = new HttpClient()) {

            var response = await client.GetAsync(ApiUri);

            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<IEnumerable<Car>>();
        }
    }
}

As you saw earlier, if there is a SynchronizationContext available, the code that the compiler generates will capture it and post the continuation back to that context to be executed. Keep this part in mind. This little class has been put inside a separate project, SampleAPI.Client, and referenced from our web clients. An ASP.NET MVC application has also been created, and it has a controller that has two actions: one calls the API asynchronously, and the other does the same in a blocking fashion (see Listing 2-19).

Listing 2-19.  An ASP.NET MVC Controller That Consumes the HTTP API Through the .NET Client

public class HomeController : Controller {

    public async Task<ViewResult> CarsAsync() {

        SampleAPIClient client = new SampleAPIClient();
        var cars = await client.GetCarsAsync();

        return View("Index", model: cars);
    }

    public ViewResult CarsSync() {

        SampleAPIClient client = new SampleAPIClient();
        var cars = client.GetCarsAsync().Result;

        return View("Index", model: cars);
    }
}

Listing 2-20 shows the cars on the web page displayed in a simple Razor view.

Listing 2-20.  The Razor View Display of the Cars on the Web Page

@model IEnumerable<SampleAPI.Client.Car>
@{
    ViewBag.Title = "Home Page";
}

<h3>Cars List</h3>

<ul>
    @foreach (var car in Model) {
        <li>
            @car.Make, @car.Model (@car.Year) - @car.Price.ToString("C")
        </li>
    }
</ul>

Navigating to /home/CarsAsync should get the result to return smoothly (see Figure 2-11).

9781430247258_Fig02-11.jpg

Figure 2-11. The web page display of the cars list

However, if you navigate to /home/CarsSync to invoke the CarsSync method, you will see that the page never comes back because a deadlock was just introduced for the reasons explained earlier.

Taking a look at our GetCarsAsync method implementation inside our .NET client for the HTTP API will show that it is completely unnecessary to get back to the current SynchronizationContext because nothing is needed from the current context. This is actually a good sign because it is not our .NET client’s concern to do anything under the current SynchronizationContext. It is, on the other hand, our consumer’s responsibility.

In the case here, the default SynchronizationContext behavior that the compiler is generating needs to be suppressed. This can be achieved with the ConfigureAwait method of the Task class, which was introduced with .NET 4.5. The ConfigureAwait method accepts a Boolean parameter named continueOnCapturedContext. By passing false into this method so as not to marshal the continuation back to the original context captured, our problem will be solved. Listing 2-21 shows the new look of our .NET client for the HTTP API.

Listing 2-21.  The New Look of Our Simple HTTP API .NET Client

public class SampleAPIClient {

    private const string ApiUri = "http://localhost:17257/api/cars";

    public async Task<IEnumerable<Car>> GetCarsAsync() {

        using (HttpClient client = new HttpClient()) {

            var response = await client.GetAsync(ApiUri)
                .ConfigureAwait(continueOnCapturedContext: false);

            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<IEnumerable<Car>>()
                .ConfigureAwait(continueOnCapturedContext: false);
        }
    }
}

As you’ll be creating HTTP APIs with the ASP.NET Web API framework, you’ll probably also create .NET client libraries to make it easy to consume the API. If you write a .NET client for your HTTP API using the new asynchronous language features, you might want to consider these facts before moving on. Otherwise, your consumers will have a hard time understanding what is really going wrong. As a general rule, if you are developing a library, the default behavior that await gives in terms of synchronization context is nearly never what you want. However, if you are developing an application such as ASP.NET MVC or ASP.NET Web API, the default behavior will nearly always be what you want.

Summary

Asynchrony is very important if you are aiming for scalability in your application when you are consuming long-running I/O intensive operations. This chapter has shown what asynchrony is in theory, where it makes sense, and how the .NET Framework and C# language features help along the way. Considering the fact that the ASP.NET Web API framework deals with asynchrony at a very high level, this chapter will give a better understanding of how to manage asynchronous methods and operations in Web API, especially with many of the extensibility points ASP.NET Web API introduces.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.191.86