Async and Await in ASP.NET 4.5

In this article, we will see how to use Asynchronous Programming using Async and Await in ASP.NET 4.5 web forms. For demonstration purposes, we will create three different WCF Services which will fetch the Customers data, Total No. of orders and the Total Products respectively. Then we will use these services in our ASP.NET web site synchronously as well as asynchronously. We will calculate the time taken by an ASP.NET web site to call these services and find out the differences between these calls.

For this demonstration, I am using –
  • Windows Server 2012
  • Visual Studio 2012
  • SQL Server 2012
Let’s get started by creating three WCF Services with the name “CustomersService”, “OrdersService” and “ProductsService”. To create these service, we will first create a WCF Service using Visual Studio 2012. Open Visual Studio 2012. Click on File > New > Web Site and create a WCF Service with the name “NorthwindService” as shown below –

newwcfsrv2

Once the WCF Service is ready, delete the default files, which are “Service.svc”, “IService.cs” and “Service.cs” files from the project.

Now right click the project and add a new Item to the project. Choose “ADO.NET Entity Data Model” and name it “Northwind”. As soon as you add the Entity data model, the IDE will display the Entity Data Model Wizard. Choose the Generate from database option in our first step and click on Next button. In this step, set up a connection to the Northwind database and save the connection string in Web.Config file. Click on “Next” button.

In this step, choose “Customers”, “Orders” and “Products” tables as shown below –

chooseentities3

Click on “Finish” button. Now the wizard will display all the selected tables into our “Northwind.edmx” file.
Let’s add three WCF Services with the name “Cu
stomersService”, “OrdersService” and “ProductsService” respectively as shown below –

addwcfsrv2

Write some code in our all three services as shown below –

Customers Service –

[ServiceContract]
public interface ICustomersService
{
    [OperationContract]
    List<Customer> FetchCustomers();
}

Import the namespace System.Threading

public class CustomersService : ICustomersService
{
    public List<Customer> FetchCustomers()
    {
        NorthwindEntities1 northwindEntity = new NorthwindEntities1();
       dataContext.Configuration.ProxyCreationEnabled = false;
        var query = from nwCustomers in northwindEntity.Customers
                    select nwCustomers;
       Thread.Sleep(3000);
        return query.ToList();
    }
}

Orders Service –

[ServiceContract]
public interface IOrdersService
{
    [OperationContract]
    int FetchTotalOrders();
}


Import the namespace System.Threading

public class OrdersService : IOrdersService
{

    public int FetchTotalOrders()
    {
        NorthwindEntities1 northwindEntity = new NorthwindEntities1();
       dataContext.Configuration.ProxyCreationEnabled = false;
        var query = from nwOrders in northwindEntity.Orders
                    select nwOrders;
   Thread.Sleep(3000);
   return query.Count();
    }
}

Products Service –

[ServiceContract]
public interface IProductsService
{
    [OperationContract]
    int FetchTotalProducts();
}


Import the namespace System.Threading

public class ProductsService : IProductsService
{

    public int FetchTotalProducts()
    {
        NorthwindEntities1 northwindEntity = new NorthwindEntities1();
       dataContext.Configuration.ProxyCreationEnabled = false;
        var query = from nwProducts in northwindEntity.Products
                    select nwProducts;
       Thread.Sleep(3000);
        return query.Count();
    }
}

Now our services are ready, let’s add a new blank web site to our solution. Add a new web form to our web site with the default name “Default.aspx”. Design the web form as shown below –

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <h1 style="background-color:#000066;color:#ffffff">Northwind History!!</h1>
        <hr />
        <asp:Repeater ID="Repeater1" runat="server">
            <ItemTemplate>
                <p>Contact Name <%#Eval("ContactName") %></p>
            </ItemTemplate>
        </asp:Repeater>
        Total No of Orders - <asp:Label ID="lblTotalOrders" runat="server" Text="Label"></asp:Label><br />
        Total No of Products - <asp:Label ID="lblTotalProducts" runat="server" Text="Label"></asp:Label><br />
        Total Time of Execution (MS)- <asp:Label ID="lblTotalExecutionTime" runat="server" Text="Label"></asp:Label>
    </div>
    </form>
</body>
</html>


Add a reference to our services with the name “CustomersProxy”, “OrdersProxy” and “ProductsProxy” as shown below –

 addsrvref2

Now let’s write some code in “Default.aspx.cs” in the Page_Load event  –
using System.Diagnostics;     

public partial class _Default : System.Web.UI.Page
{
    CustomersProxy.CustomersServiceClient clientCustomers = new CustomersProxy.CustomersServiceClient();
    OrdersProxy.OrdersServiceClient clientOrders = new OrdersProxy.OrdersServiceClient();
    ProductsProxy.ProductsServiceClient clientProducts = new ProductsProxy.ProductsServiceClient();
    protected void Page_Load(object sender, EventArgs e)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
       
        var customerResult = clientCustomers.FetchAllCustomers();
        var ordersCount = clientOrders.TotalOrders();
        var productsCount = clientProducts.TotalProducts();
        sw.Stop();
   long executionTime=sw.ElapsedMilliseconds;
        Repeater1.DataSource = customerResult;
        Repeater1.DataBind();
        lblTotalOrders.Text = ordersCount.ToString();
        lblTotalProducts.Text = productsCount.ToString();
        lblTotalExecutionTime.Text = executionTime.ToString();
    }
}


The above code is giving a call to the service synchronously. If you now run your “Default.aspx” page, you will see the records of Customers, total no. of records in Orders table and total no. of records in Products table. It will also show you the total execution time. In my case it took 12427 milliseconds.

Now let’s test the same code in terms of Asynchronous call. We will now modify the Page_Load event which will now give a call to the WCF services asynchronously as shown below –
First let’s add async attribute to our Page Directive as shown below –

pagedirective5

Now let’s write below code in our code file.

using System.Threading.Tasks;

public partial class _Default : System.Web.UI.Page
{
    CustomersProxy.CustomersServiceClient clientCustomers = new CustomersProxy.CustomersServiceClient();
    OrdersProxy.OrdersServiceClient clientOrders = new OrdersProxy.OrdersServiceClient();
    ProductsProxy.ProductsServiceClient clientProducts = new ProductsProxy.ProductsServiceClient();
    protected async void Page_Load(object sender, EventArgs e)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
       
        var customerResult = clientCustomers.FetchAllCustomersAsync();
        var ordersCount = clientOrders.TotalOrdersAsync();
        var productsCount = clientProducts.TotalProductsAsync();
        await Task.WhenAll(customerResult, ordersCount, productsCount);
        sw.Stop();
        long executionTime=sw.ElapsedMilliseconds;                    
        Repeater1.DataSource = customerResult.Result;
        Repeater1.DataBind();
        lblTotalOrders.Text = ordersCount.Result.ToString();
        lblTotalProducts.Text = productsCount.Result.ToString();
        lblTotalExecutionTime.Text = executionTime.ToString();
    }
}


In the above code, we are calling our WCF methods asynchronously. Each method returns Task<Type>. For example “TotalOrdersAsync()” method return us awaitable Task<int>.
Now we will have to get the actual result from the returned task by our service method. We can get the actual value by Result property of our Task<Type> as shown in the above code.

Task.WhenAll() method creates a task after completion of all the tasks supplied to it. We are passing all the three tasks to this method.

This time around, it took 6431 Milliseconds to execute all the service methods.
You may also want to read Task-Based Asynchronous Pattern in .NET 4.5

Summary – In this article we have seen how to use Asynchronous Programming using Async and Await in ASP.NET 4.5 web forms.

The entire source code of this article can be downloaded over here (GitHub)





1 comment:

Anonymous said...

Can't the same be accomplished using Async API method of the webservice and the Lambda expression in C# to make the call? THats pretty easy as well...