Exercise 3: Asynchronous Page Processing in ASP.NET Web Forms

In this exercise, you will be introduced to the new asynchronous page processing features in ASP.NET Web Forms.

Task 1 – Updating the Product Details Page to Upload and Show Images

In this task, you will update the product details page to allow the user to specify an image URL for the product and display it in the read-only view. You will create a local copy of the specified image by downloading it synchronously. In the next task, you will update this implementation to make it work asynchronously.

  1. Open Visual Studio 11 and load the WebFormsLab-Ex3-Begin.sln solution located in Source\Ex3-Async\Begin from this lab’s folder. Alternatively, you can continue working on your existing solution from the previous exercises.
  2. Open the ProductDetails.aspx page source and add a field in the FormView’s ItemTemplate to show the product image.

    (Code Snippet – Web Forms Lab - Ex03 – Show Product Image)

    HTML

    <ItemTemplate>
    FakePre-9bb4fd05520c4f80be66f030bd858d24-d9e6c9688a3e49368a6e3b23c4fb984aFakePre-0e713120fa7b421d98fa2fb47332b915-a8f9aa1819b74a3eae155ccba40c7951FakePre-363da2e94d7844e78b563bc62a89b7d4-f44964015cb14511b6a9277a477c61b5FakePre-23337b300a034abc95b5d9e464a55070-14976926d3c143979f858834d2d6a4fcFakePre-27d60bfde5c24ed0ad8ade5b4a99d6da-3a2ba81b3c184909b1052ec1c45d1563FakePre-48f9565459db4d4181f675e130ccf1d1-29f0686f43c84eb7b36e090232b9d2d1FakePre-074fd3c796474c8d8e468cea12ccb86e-3a74d834684045f9bd741484a726af16FakePre-149f10fcee2d49aa89269f2bb6509442-31d9542bd7574c8c9f41875ec22d0462FakePre-42c44d373d2c48938111eb11f52ba439-7f590ac9b5aa4e7f881e414ee0c175d7 <li><b><asp:Label ID="Label5" runat="server" AssociatedControlID="itemUnitPrice">Image:</asp:Label></b></li> <li><img src="<%# string.IsNullOrEmpty(Item.ImagePath) ? "/Images/noimage.jpg" : Item.ImagePath %>" alt="Image" /></li>FakePre-de2442987259451cbe9b43be28ce0c21-bdeb21e4b84e4810b4b37691f3b8df97FakePre-6df2226c07a54fb0b7efb4bdd546d223-bb567641f674456e83b39dd6e22977c4FakePre-0c00a0429a764e2093295065402b1211-b94db909ae4842c08e7411bd6cb7c1dcFakePre-99525689e20e4490a80fb8fe7a61d790-1ff9dff2910c4212a58af24737f04eabFakePre-f2f92ac56c4543f9b4ef71a2eacb9843-47a3a5ec0ad84ba1a65825895268215cFakePre-e5bdb3325079425eb059f37afcccea3c-67989012a5594e3d8ffbe0b47f6f2731FakePre-a6126b2d2c234622b8110a4b9b27b968-0700b7e3553a4068b39f2a87414afbf0FakePre-fb4e47423a074139863ab1b52b94f2a4-6d8706cb45d24dc294d1b88b939d2261FakePre-5563fdc47ca745e391b55264922c1f25-b7ec3f940c824e249083290e4d6fb86dFakePre-fcb1e1352b4246ea822b08b4b6a2078a-b3de8e498e644887899daa586111be2f

  3. Add a field to specify the image URL in the FormView’s EditTemplate.

    (Code Snippet – Web Forms Lab - Ex03 – Edit Image URL)

    HTML

    <EditItemTemplate>
    FakePre-31955c15ecce46df975e73e47d07205c-4782245e1eb04218bbe682c3c6af83b7FakePre-b610200dc74f40b290b22d8484806b10-af206729ae8a4047b867e639552d3b4fFakePre-1545d9e871894ad583c6f290016b9782-6da6a31dc8964111af56c19cbe05fed8FakePre-173682568dda44a5b736d2952d5e511c-a605066ababd4f8cb9d1da8e0badd28aFakePre-a0bb3babf1cc4840bd5cd4e14612bbaf-207d632e0e564e9c8dfcec38570576a4FakePre-536086b320ea4f32a023e81578b3b1b7-a927f6662a64405f9326de164fb293ffFakePre-068c7a03c6ac4fca9128933867eb5ca4-5bc1984f719f460d815ea5ca49e30532FakePre-2ec2b9239ff64e49a6c5e5cf4678567b-5b25c74918924fefa0225ef6b0dbfb45FakePre-e069f3f572794fbda439d9db02666637-57708d513a7343c7a73ffea70199a791FakePre-360b524283eb4fe4a7556bfc22c83ab8-6be973cf6ec54930b5da8bc97f1a6005 <li><asp:Label ID="Label1" runat="server" AssociatedControlID="ImagePath">Image URL:</asp:Label></li> <li><asp:TextBox runat="server" ID="ImagePath" Text='<%#: BindItem.ImagePath %>' /></li>FakePre-ffb4f47357334472b8ac363b983d044e-5c4e1272548a4128a55c5d0f54a20630FakePre-f5f0961134e94ef392c361f7363280b3-a9ddc612c18d4c489058d22bea64edacFakePre-52f867d5da0549988e975a4142eb131f-311200e102644fae8c48dd8dc9729577FakePre-fa226c7a1676488a9a4c9c4fa92f2e78-aa1fe422f9c7461ebb15b3c702f5f882FakePre-c9abf2f5f49744cbabfd38fa74234335-37220d8179034b11bc5b53aeabd3dbf6FakePre-904672a3972a46e1bb901b4d9c76b11b-955651fae0484be193e663444afde0c2FakePre-c99ec95cf7e44d9d8db57fc539917ea8-2dce0cf580494f23a46474e72425142fFakePre-b18e1eacc10e43868c53aa0008def5c2-33a27aa2af764fc5be568b884aafc0caFakePre-5c1b6b001f9f48d590eef642e55b0078-5668e4ccac624a35b2762afcf9631c56FakePre-ae1a0c7d740c4ba38afa51224f80a2fe-568f9da467ca4b84bb202fad9ffc1745

  4. Open the ProductDetails.aspx.cs code-behind file and create an UpdateProductImage method to store remote images in the local Images folder and update the product entity with the new image location value.

    (Code Snippet – Web Forms Lab - Ex03 – UpdateProductImage)

    C#

    private void UpdateProductImage(Product product) { string imageUrl = product.ImagePath; if (!string.IsNullOrEmpty(imageUrl) && !VirtualPathUtility.IsAbsolute(imageUrl)) { product.ImagePath = string.Format("/Images/{0}{1}", product.ProductId, Path.GetExtension(imageUrl)); using (var wc = new WebClient()) { wc.DownloadFile(imageUrl, Server.MapPath(product.ImagePath)); } } }
    FakePre-16a0f61e3a8c4aecab75def2a0a9ebd1-382714df7c044919995e1f5829e5b5b2
    

  5. Update the UpdateProduct method to call the UpdateProductImage method.

    (Code Snippet – Web Forms Lab - Ex03 – UpdateProductImage Call)

    C#

    public void UpdateProduct(int productId)
    FakePre-3af49cded1eb428dbd3151c119833398-3c5c01c1078a4ca38b54ed0214c2fd82FakePre-23428540b2774d0dbdd41fde24ef1097-521046d5db81468ab0426ca83ccec315FakePre-55bfce47ca464ceb8e6731f29f70cb33-d768d620593b4e8f9c7d8f0c9c18a0aeFakePre-49ac4651595e4ebfbb456cc9aa045e65-44880b6c296e45df83df567a2a78084bFakePre-def9ed31b4684b65a623456ae3906eaa-d4d04546b57b4dc58574c9562b0e033e this.UpdateProductImage(product);FakePre-026822897e1e41238d7c5d3163f8f557-7f2e97519dea4ff0bf8be978857bcce1FakePre-1ee9de1e7c164a28ba6199e881438c9f-0ae0312b39e245d99b38d5ec3d9fd697FakePre-9cd9668651204a1e88f924a2bfa6bb44-051fc7b40de443a8a2f434f00a805026FakePre-0d39017df71c4b138a0d227b92163035-676019744bd44a55a6bbb3fb34f8e177FakePre-32d04c6aa46d49f7a194284751ceaff0-b91865276fc845bda9fdd30510babadbFakePre-0226b697dda64e03968bd5f5d9c2765c-b15c1bb8b23d4a05b9630a8a0d248043

  6. Add the following namespace directives.

    (Code Snippet – Web Forms Lab - Ex03 – Namespaces)

    C#

    using System.Net; using System.IO;

  7. Run the application and try to upload an image for a product. For example, you can use the following image URL from Office Clip Arts: https://officeimg.vo.msecnd.net/en-us/images/MB900437099.jpg

    Figure 24

    Setting an image for a product

Task 2 – Adding Asynchronous Processing to the Product Details Page

In this task, you will update the product details page to make it work asynchronously. You will enhance a long running task – the image download process – by using ASP.NET 4.5 asynchronous page processing.

Asynchronous methods in web applications can be used to optimize the way ASP.NET thread pools are used. In ASP.NET there are a limited number of threads in the thread pool for attending requests, thus, when all the threads are busy, ASP.NET starts to reject new requests, sends application error messages and makes your site unavailable.

Time-consuming operations on your web site are great candidates for asynchronous programming because they occupy the assigned thread for a long time. This includes long running requests, pages with lots of different elements and pages that require offline operations, such querying a database or accessing an external web server. The advantage is that if you use asynchronous methods for these operations, while the page is processing, the thread is freed and returned to the thread pool and can be used to attend to a new page request. This means, the page will start processing in one thread from the thread pool and might complete processing in a different one, after the async processing completes.

  1. Open the ProductDetails.aspx page. Add the Async attribute in the Page element and set it to true. This attribute tells ASP.NET to implement the IHttpAsyncHandler interface.

    HTML

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    FakePre-5095e040e4cc4029aa2312d688d6b1e4-da1ee1de47274c4f811bee1998087225 Async="true" %>

  2. Add a Label at the bottom of the page to show the details of the threads running the page.

    (Code Snippet – Web Forms Lab - Ex03 – Threads Message Label)

    HTML

        <EmptyDataTemplate>Product not found</EmptyDataTemplate>
    FakePre-21fdb0c4d52942ea8241d242a91a437e-63429b8bfa964b559b4b0e22fb02de2cFakePre-3f162bcd51dd4d77a7befca93545f67d-06f7d5a1c0274a678d6917f27b6e2377 <asp:Label ID="threadsMessageLabel" runat="server" />FakePre-6b78135ec6d04f2d9870495715ef2487-a0a8eacb1e7042b789223f30801adbc6

  3. Open up ProductDetails.aspx.cs and add the following namespace directive.

    (Code Snippet – Web Forms Lab - Ex03 – Namespaces 2)

    C#

    using System.Threading;

  4. Modify the UpdateProductImage method to download the image with an asynchronous task. You will replace the WebClientDownloadFile method with the DownloadFileTaskAsync method and include the await keyword.

    (Code Snippet – Web Forms Lab - Ex03 – UpdateProductImage Async)

    C#

    private void UpdateProductImage(Product product)
    FakePre-62b53179e32a456d906b6807b9124f71-021559a71c714000bca293902319f09eFakePre-079b7031112e462bbda03a2c51153ae1-aa1c22c34f5a414297dcb53d364ec865FakePre-98c76d990f1a4a6daead21819d025910-f7988cade7ac4569bf63d964f40cb8e1FakePre-b8a906ebe88545a9ae0b2e97c0f1cd01-fb48fbc914d54725bb74fd857f99672cFakePre-8eb7eb9e5efd42028c252117a6ce4fe1-3a5f0b966dbd44adba9a9e6ea24ccdd1FakePre-5a074634f9c042c9a7e042af45e5b55b-fa2ff42f1c0641a7a209da122e629d1cFakePre-b3a7f6ab7f3d42fdab009b31266c31ed-3ca907c0a958485ab697a47141f9c12e RegisterAsyncTask(new PageAsyncTask(async(t) => { using (var wc = new WebClient()) { await wc.DownloadFileTaskAsync(imageUrl, Server.MapPath(product.ImagePath)); } }));FakePre-b101dcf014924d27b2f70091484cb3c1-e77edd41354d4b3bad0466b9c1ef90e9FakePre-dd397f8295644609bee683318048adab-c0e5932be0264fff81b02c2b5d2129f3

    The RegisterAsyncTask registers a new page asynchronous task to be executed in a different thread. It receives a lambda expression with the Task (t) to be executed.

    The await keyword in the DownloadFileTaskAsync method converts the remainder of the method into a callback that is invoked asynchronously after the DownloadFileTaskAsync method has completed. ASP.NET will resume the execution of the method by automatically maintaining all the HTTP request original values. The new asynchronous programming model in .NET 4.5 enables you to write asynchronous code that looks very much like synchronous code, and let the compiler handle the complications of callback functions or continuation code.

    Note:
    RegisterAsyncTask and PageAsyncTask were already available since .NET 2.0. The await keyword is new from the .NET 4.5 asynchronous programming model and can be used together with the new TaskAsync methods from the .NET WebClient object.

  5. Add code to display the threads on which the code started and finished executing.

    (Code Snippet – Web Forms Lab - Ex03 – Show threads)

    C#

    private void UpdateProductImage(Product product)
    FakePre-f843edbfb6f94182bd66dfcc9806b696-c03eff44b5884f75835e214a1438bcb6FakePre-1fdc5b88a6404e3a811b3b508c9184e9-47d0319d22184e47af12c26c79ec4723FakePre-cd46ed31f8634707ae193681cfb5724f-60e9263ba72a41c7b18a464274d5e8d2FakePre-99dec0a4de1c4a4192f6cf6e1418dfe6-47dc1b16454e45a18f3954edd1ea0724FakePre-c25ef4b9131042ec9b6ef67afa6f118c-08883235595e4104b3ea0ae5ad2a1c38FakePre-55be52f4327b4bce87aad378751007b5-ffea21e5a3e949e699d042e6b70ca194FakePre-c928e106480a47eabc0c2a3fc5f14a50-fad51befcc744d5a87b7ce04c1a80db3FakePre-48e5cd3084ff4600b6d8b296c185e693-e73f4ad0603049d79eec4e6f06fed72eFakePre-b2c9e7f5cc924d729f3bcc4c221125c9-be3f1b3f49ff4bf18a986f17c2a68c74 var startThread = Thread.CurrentThread.ManagedThreadId; using (var wc = new WebClient()) { await wc.DownloadFileTaskAsync(imageUrl, Server.MapPath(product.ImagePath)); } var endThread = Thread.CurrentThread.ManagedThreadId; threadsMessageLabel.Text = string.Format("Started on thread: {0}<br /> Finished on thread: {1}", startThread, endThread);FakePre-d48a65cfe9a249d090d196d05374f7f5-20b82ab2329c4481a6b6cfe3ccc99354FakePre-f28d07b2d03a46cea043b16915b6a813-a35c4a57469f4166a81b4f7ce745ecebFakePre-1ae773b8fe5742628dbf31c68cb19df7-2fcb0ab3563344079c5208abfb05a9efFakePre-12ddffefd1814d6696cf45e0d04cc398-d45cb996f25541efa57019dcdc4f894bFakePre-6ac86b3c6d974021b96f99ebdcd50853-56e345bdc4c24fa58105f079f098e050FakePre-b602555a82b343f8a8e5dc79c837d958-4bab4f0902374d22b0c59e2316c804df

  6. Open the web site’s web.config file. Add the following appSetting variable.

    XML

    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>

  7. Press F5 to run the application and upload an image for the product. Notice the threads ID where the code started and finished may be different. This is because asynchronous tasks run on a separate thread from ASP.NET thread pool. When the task completes, ASP.NET puts the task back in the queue and assigns any of the available threads.

    Figure 25

    Downloading an image asynchronously