Exercise 4: Using Asynchronous Controllers

Microsoft .NET Framework 4.5 introduces new language features in C# and Visual Basic to provide a new foundation for asynchrony in .NET programming. This new foundation makes asynchronous programming similar to – and about as straightforward as – synchronous programming.

You are now able to write asynchronous action methods in ASP.NET MVC 4 by using the AsyncController class. You can use asynchronous action methods for long-running, non-CPU bound requests. This avoids blocking the Web server from performing work while the request is being processed. The AsyncController class is typically used for long-running Web service calls.

This exercise explains the basics of asynchronous operation in ASP.NET MVC 4. If you want a deeper dive, you can check out the following article: http://msdn.microsoft.com/en-us/library/ee728598.aspx

Task 1 – Implementing an Asynchronous Controller

  1. Open Visual Studio 11 if not already opened.
  2. Open the MVC4Lab-Ex4-Begin.sln solution located in Source\Ex4-Async\Begin from this lab’s folder. Alternatively, you can continue working on your existing solution from the previous exercise.
  3. If you opened the MVC4Lab-Ex4-Begin.sln solution, right-click the solution and select Enable NuGet Package Restore

    Note:
    For more information see the following article http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages

  4. Open the HomeController.cs class from the Controllers folder.
  5. Add the following using statement.

    C#

    using System.Threading.Tasks;

  6. Update the HomeController class to inherit from AsyncController. Controllers that derive from AsyncController enable ASP.NET to process asynchronous requests, and they can still service synchronous action methods.

    C#

    public class HomeController : AsyncController
    {

  7. Add the async keyword to the Index method and make it return the type Task<ActionResult>.

    C#

    public async Task<ActionResult> Index()
    {
    FakePre-e8f87c3cc9ae4c9a9888e41da244b515-a067aed13d2746c2b1295d8a71499641

    Note:
    The async keyword is one of the new keywords the .NET Framework 4.5 provides; it tells the compiler that this method contains asynchronous code.

    A Task object represents an asynchronous operation that may complete at some point in the future.

  8. Replace the client.Get() calls with the async version client.GetAsync() as shown below.

    (Code Snippet – MVC4 Lab - Ex04 - GetAsync)

    C#

    public async Task<ActionResult> Index()
    FakePre-8f3ad7d3877346238b1bec480603a2b0-1f07c5b87be4451f8223b5bf83b937fdFakePre-6b1fbcae69db438090bf135c8f0e1268-a63fca1b182a4ce38ce2eb43c7fbf13a var response = await client.GetAsync(Url.Action("gallery", "photo", null, Request.Url.Scheme));FakePre-b9e376c58d024dd984912bbaf9761b39-0a356411108f42e8a224a4aeb35ff448

    Note:
    In the previous code, you are using the asynchronous version of the Get method - GetAsync - which is a new method added by the .NET Framework 4.5 and has different signature since it returns a Task type object.

    Adding the await keyword tells the compiler to asynchronously wait for the task returned from the method call. This means that the rest of the code will be executed as a callback only after the awaited method completes. Another thing to notice is that you do not need to change your try-catch block in order to make this work: the exceptions that happen in background or in foreground will still be caught without any extra work using a handler provided by the framework.

  9. Change the code to continue with the asynchronous implementation by commenting out the latter two lines of the method and replacing them with the new code as shown below

    (Code Snippet – MVC4 Lab - Ex04 - ReadAsStringAsync)

    C#

    //var jss = new JavaScriptSerializer();
    FakePre-976ee480d4a4491c88e3510ff9b0cc22-48b7e903fbb74bb8ac6740af457ae4d8FakePre-d3b77581e3e542ba9773575f922f5c25-43e13901d28c4ed3b6ae2ee71cccb109var jss = new JavaScriptSerializer(); var responseString = await response.Content.ReadAsStringAsync(); var result = jss.Deserialize<List<Photo>>(responseString);FakePre-3c0624c970a74fd4a87db0d78e57d390-4b938aa38af4477e9c97dedc1323b89a

  10. Run the application. You will notice no major changes, but your code will not block a thread from the thread pool making a better usage of the server resources and improving performance.
    Note:
    You can learn more about the new asynchronous programming features in the lab “Asynchronous Programming in .NET 4.5 with C# and Visual Basic” included in the Visual Studio 11 Training Kit.

Task 2 – Handling Time-Outs with Cancellation Tokens

Asynchronous action methods that return Task instances can also support time-outs. In this task, you will update the Index method code to handle a time-out scenario using a cancellation token.

  1. Go back to Visual Studio and press SHIFT+F5 to stop debugging.
  2. Add the following using statement to the HomeController.cs file.

    C#

    using System.Threading;

  3. Update the Index action to receive a CancellationToken argument.

    C#

    public async Task<ActionResult> Index(CancellationToken cancellationToken)
    FakePre-f700d66124d140768b41c74fd7a15555-9a6f1fc2d3fd4d9a833c849e9033f2dcFakePre-82297870c90e42efb95b7a4abc35562c-f30e7d8bae614a0c984f6d844a9bc32e

  4. Refactor the service calls to use SendAsync instead of GetAsync and receive the cancellation token.

    (Code Snippet – MVC4 Lab - Ex04 - SendAsync with CancellationToken)

    C#

    public async Task<ActionResult> Index(CancellationToken cancellationToken)
    FakePre-ad95e35db4c54578aa0c67ddd2eb328e-97d624ef79d44997a9c1126d6ac134d6FakePre-1cbbf47256494fe3a1c45f4b5f3b305a-7503f7f8589f44319f4948490998fe7b var photosRequest = new HttpRequestMessage(HttpMethod.Get, Url.Action("gallery", "photo", null, Request.Url.Scheme)); var response = await client.SendAsync(photosRequest, cancellationToken); FakePre-17a1fe11bf324ad593f65e57d478cce5-510404a44dbe4aa982d1a69897a4007cFakePre-3f0501d6ceb84ab9832cc08b763d9820-33ea15154ca04b83815ac48dff49255fFakePre-5f7f9be102c6486f9ef9ecf7c7a6eefd-11c8faadcb014779918f92bd2f3ee0aaFakePre-7ddb16577bb34c3986f0fe4b3ea02e33-ee24774c076f46c38152c0e631ed55f4FakePre-cf8c6432cbf2441191b61678ff2e41b9-eb52d4a17a3346a7a3210b4c3e549a11

  5. Decorate the Index method with an AsyncTimeout attribute set to 500 milliseconds and a HandleError attribute configured to handle TaskCanceledException by redirecting to a TimedOut view.

    (Code Snippet – MVC4 Lab - Ex04 - Attributes)

    C#

    [AsyncTimeout(500)] [HandleError(ExceptionType = typeof(TaskCanceledException), View = "TimedOut")]
    public async Task<ActionResult> Index(CancellationToken cancellationToken)
    FakePre-5b17991ab05d4ca6b29d68e77a55b829-7085e360c5cb453ca2694c71fc6a8a26

  6. Open the PhotoController class and update the Gallery method to delay the execution 1000 miliseconds (1 second) to simulate a long running task.

    C#

    public ActionResult Gallery()
    FakePre-34df13fe76cc4577921a894661dd5ef6-40460153fc6843699527a75d05e55e13 System.Threading.Thread.Sleep(1000);FakePre-df4924e50fcc4c80981fb09acc301223-567c75f4152b466c9b3c9fc46e87ad19FakePre-a1b3a96df11844a18d23e7c3bfdae1d3-bfff41e261274d7daa073c187f2c0973FakePre-517fd86020064aedb5b1516553f7ef1b-73ad0c0eb6074fd9b2d1ece187da7906

  7. Open the Web.config file and enable custom errors by editing the following property.

    C#

    <system.web>
    <customErrors mode="On"></customErrors>FakePre-5bc806608b7846c9b527d0e316beb23c-2ad58decb58942ad968536278ed01123

  8. Create a new view in Views\Shared named TimedOut and use the default layout. In the Solution Explorer, right-click the Views\Shared folder and select Add | View.

    Figure 41

    Using different views for each mobile device

  9. Update the TimedOut view content as shown below.

    HTML

    @{
    FakePre-0d601bbb8d164f6c88bafb7dec9e2c42-4e50c714232b4f36827d227da7c23c58FakePre-36a5a97914544a6d8eb34877537c93d1-d5d4910c4a6f48d8a915907c2e26811fFakePre-b5a636a6cce0484d83d20fe31f0062a0-40dc2310589b414497f18b63b5372a1cFakePre-55d8d88674604d15b473737bc12081a4-68c42ac238c34e45bb15f91e45e65fa1<h2>Timed Out!</h2>

  10. Run the application and navigate to the root URL. As you have added a Thread.Sleep of 1000 milliseconds, you will get a time-out error, generated by the AsyncTimeout attribute and catch by the HandleError attribute.

    Figure 42

    Time-out exception handled