Asynchronous Programming with Async and Await in ASP.NET and C#

Performance is one of the most important thing we developers keep in our mind while developing any type of applications, be it a web application or a windows application or a mobile app.

Performance depends on various parameters but here in this article we will see how Asynchronous Programming will help us in achieving better performance for our application by usage of async and await key words.

In this article we will see what is asynchronous programming and how can we achieve it through async and await keywords in ASP.NET and C#.

What is Asynchronous Programming?


As the name suggests, asynchronous means occurring at the same time. In programming language we can define it like; more than one functionalities are being executed at the same time.

Asynchronous Programming improves responsiveness of the entire application. It is essential for activities those are potential blocking such as accessing some web resources, reading a large file or images, sending multiple emails etc. In Traditional programming such activity is blocked within a synchronous process, hence the entire application has to wait till the process ends. But in asynchronous process the application can continue with some other tasks that is not dependent on the time taking process.




Let's take an example to clear the puzzle around Asynchronous programming.

Consider we have two methods: Method 1, responsible for inserting enquiry record into data base and Method 2, responsible to send an email from an enquiry form.

In this case both methods are independent. Once we got an enquiry from our website we want to save that record in Database and also at the same time we want to send a notification mail to the admin about the enquiry.

Below is the code for this situation. Check it and I will explain bit by bit.
protected void btnSendQuote_Click(object sender, EventArgs e)
  {
      ContactDetails objContactDetails = new ContactDetails();

      objContactDetails.Name = txtQuoteName.Text.Trim();
      objContactDetails.Email = txtQuoteEmail.Text.Trim();
      objContactDetails.Phone = txtQuotePhone.Text.Trim();
      objContactDetails.Message = txtQuoteMessage.Text.Trim();

      int retId = AddContactDetails(objContactDetails);

      bool isSent = SendNotificationEmail(objContactDetails);


      if (retId > 0 && isSent == true)
      {
          Response.Redirect("thank-you.aspx");
      }
      else
      {
          Response.Redirect("error.aspx");
      }
  }

  public int AddContactDetails(ContactDetails objContactDetails)
  {
      using (SqlConnection sqlConnection = new SqlConnection(_dbConnectionString))
      {
          using (SqlCommand sqlCommand = new SqlCommand("SaveContactDetail", sqlConnection))
          {
              sqlCommand.CommandType = CommandType.StoredProcedure;
              sqlCommand.Parameters.Add("@Name", SqlDbType.VarChar).Value = objContactDetails.Name;
              sqlCommand.Parameters.Add("@Phone", SqlDbType.VarChar).Value = objContactDetails.Phone;
              sqlCommand.Parameters.Add("@Email", SqlDbType.NVarChar).Value = objContactDetails.Email;
              sqlCommand.Parameters.Add("@Message", SqlDbType.NVarChar).Value = objContactDetails.Message;

              sqlConnection.Open();

              return Convert.ToInt32(sqlCommand.ExecuteScalar());
          }
      }
  }

  public bool SendNotificationEmail(ContactDetails objContactDetails)
  {
      //Assign the smtp credentials for gmail
      SmtpClient smtp = new SmtpClient();
      if (true)
      {
          smtp.Host = "smtp.zoho.com";
          smtp.Port = 587;
          smtp.EnableSsl = true;
          smtp.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
          smtp.UseDefaultCredentials = false;
          smtp.Credentials = new NetworkCredential("[email protected]", "Password");
          smtp.Timeout = 10000;
      }

      MailAddress fromAddress = new MailAddress("[email protected]", "Enquiry From iCodeFor.NET");      MailAddress toAddress = new MailAddress("[email protected]", "Your Name");

      //Passing values to smtp object
      dynamic message = new MailMessage(fromAddress, toAddress);


      message.Subject = "[Reminder]Need to take action in " + objContactDetails.TimeDifference + " min for '" + objContactDetails.ClientName + "'";
      message.Body = "<p>Hello Mama, <br/><br/>" +
                     "You need to take action against below reminder:<br/><br/>" +
                     "<table  border='1' style='min-width:700px;border-collapse: collapse;border-color: #ddd;'><tr> <td align = 'left' style='width:110px;'> <strong>Info Header </strong></td> <td align = 'left'> <strong>Details</strong> </td>" +
                     "<tr><td align = 'left'>Client Name: </td><td align = 'left'>" + objContactDetails.ClientName + "</tr>" +
                     "<tr><td align = 'left'>Email: </td><td align = 'left'>" + objContactDetails.Email + "</tr>" +
                     "<tr><td align = 'left'>Phone: </td><td align = 'left'>" + objContactDetails.Phone + "</tr></table>" +
                     "<tr><td align = 'left'>Message: </td><td align = 'left'>" + objContactDetails.Message + "</tr></table>" +
                     "<br/> Thanks <br/> TaxPhobia</p>";
      message.IsBodyHtml = true;
      message.Priority = MailPriority.High;

      //Send email
      smtp.Send(message);

      return true;
  }
As you can see in the above code; The button click event btnSendQuote_Click is responsible to call both the send email and save data to database method.

So what happens there is, during the course of execution of AddContactDetails method the application gets blocked and SendContactEmail method waits till the completion of the previous method although there is no dependency between these two methods.



This was an example of Synchronous Program, where one method has to wait for the previous method to get executed.

In Asynchronous programming we can make these two methods execute at the same time without waiting for each other. Which in turn improves the performance and the entire application has not to be blocked during the course of execution.

Why to choose Asynchronous Programming ?


Usually in an synchronous application, the entire application becomes unresponsive until all the processes/methods are fully executed. Consider the above application; during the execution of both the methods the application gets stuck and do not allow you to resize the window, to navigate to any other page etc. basically it waits till the end of execution and becomes unresponsive.

So, to address this issue we can run all the methods in parallel by using the simple thread programming but it will block UI and wait to complete all the tasks. To come out of this problem, we have to write too many codes in traditional programming but if we will simply use the async and await keywords, then we will get the solutions in much less code.



Lets Convert the above program into an asynchronous one by using async and await keyword.
protected void btnSendQuote_Click(object sender, EventArgs e)
  {
      CallMethodsAsync();
  }

  public async void CallMethodsAsync()
  {
      ContactDetails objContactDetails = new ContactDetails();

      objContactDetails.Name = txtQuoteName.Text.Trim();
      objContactDetails.Email = txtQuoteEmail.Text.Trim();
      objContactDetails.Phone = txtQuotePhone.Text.Trim();
      objContactDetails.Message = txtQuoteMessage.Text.Trim();

      Task<int> taskSaveToDB = AddContactDetails(objContactDetails);

      Task<bool> taskSendEmail = SendNotificationEmail(objContactDetails);

      int retId = await taskSaveToDB;
      bool isSent = await taskSendEmail;

      if (retId > 0 && isSent == true)
      {
          Response.Redirect("thank-you.aspx");
      }
      else
      {
          Response.Redirect("error.aspx");
      }
  }

  public async Task<int> AddContactDetails(ContactDetails objContactDetails)
  {
      using (SqlConnection sqlConnection = new SqlConnection(_dbConnectionString))
      {
          using (SqlCommand sqlCommand = new SqlCommand("SaveContactDetail", sqlConnection))
          {
              sqlCommand.CommandType = CommandType.StoredProcedure;
              sqlCommand.Parameters.Add("@Name", SqlDbType.VarChar).Value = objContactDetails.Name;
              sqlCommand.Parameters.Add("@Phone", SqlDbType.VarChar).Value = objContactDetails.Phone;
              sqlCommand.Parameters.Add("@Email", SqlDbType.NVarChar).Value = objContactDetails.Email;
              sqlCommand.Parameters.Add("@Message", SqlDbType.NVarChar).Value = objContactDetails.Message;

              sqlConnection.Open();

              return await Convert.ToInt32(sqlCommand.ExecuteScalar().ConfigureAwait(false));
          }
      }
  }

  public async Task<bool> SendNotificationEmail(ContactDetails objContactDetails)
  {
      //Assign the smtp credentials for gmail
      SmtpClient smtp = new SmtpClient();
      if (true)
      {
          smtp.Host = "smtp.zoho.com";
          smtp.Port = 587;
          smtp.EnableSsl = true;
          smtp.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
          smtp.UseDefaultCredentials = false;
          smtp.Credentials = new NetworkCredential("[email protected]", "Password");
          smtp.Timeout = 10000;
      }

      MailAddress fromAddress = new MailAddress("[email protected]", "Enquiry From iCodeFor.NET");      MailAddress toAddress = new MailAddress("[email protected]", "Your Name");

      //Passing values to smtp object
      dynamic message = new MailMessage(fromAddress, toAddress);


      message.Subject = "[Reminder]Need to take action in " + objContactDetails.TimeDifference + " min for '" + objContactDetails.ClientName + "'";
      message.Body = "<p>Hello Mama, <br/><br/>" +
                     "You need to take action against below reminder:<br/><br/>" +
                     "<table  border='1' style='min-width:700px;border-collapse: collapse;border-color: #ddd;'><tr> <td align = 'left' style='width:110px;'> <strong>Info Header </strong></td> <td align = 'left'> <strong>Details</strong> </td>" +
                     "<tr><td align = 'left'>Client Name: </td><td align = 'left'>" + objContactDetails.ClientName + "</tr>" +
                     "<tr><td align = 'left'>Email: </td><td align = 'left'>" + objContactDetails.Email + "</tr>" +
                     "<tr><td align = 'left'>Phone: </td><td align = 'left'>" + objContactDetails.Phone + "</tr></table>" +
                     "<tr><td align = 'left'>Message: </td><td align = 'left'>" + objContactDetails.Message + "</tr></table>" +
                     "<br/> Thanks <br/> TaxPhobia</p>";
      message.IsBodyHtml = true;
      message.Priority = MailPriority.High;

      //Send email
      await smtpClient.SendMailAsync(message);

      return true;
  }
In the above code we have converted the synchronous program into an asynchronous program by the help of async and await keywords.

The return type of an async method is Task, Below code holds the returned TResults from the async method.
Task<int> taskSaveToDB = AddContactDetails(objContactDetails);

 Task<bool> taskSendEmail = SendNotificationEmail(objContactDetails);
And these two lines are responsible for parallel execution of both AddContactDetails and SendNotificationEmail methods, without waiting for each other.



But during the comparison of the returned values we need to wait for both methods so there we have to use the await keyword like below.
int retId = await taskSaveToDB;
 boolisSent = await taskSendEmail;
Here is the steps to understand the execution of this asynchronous program.
  1. The button click event named btnSendQuote_Click calls the CallMethodsAsync async method
  2. Now CallMethodsAsync has two async methods to call one for Database operation and another one for sending email.
  3. So now AddContactDetails is being called asynchronously which executes the sql command and here it should wait for some time for Db connection and operation and to avoid that blocking it yeilds control to its caller CallMethodsAsync.
  4. Same for the SendNotificationEmail; when this async method is called it also yields the control to its caller CallMethodsAsync 
  5. Now as both the async methods are running simultaneously we can call any synchronous method but here we do not have any. Hence we have to now wait for both the operations return value in order to make the redirection.
  6. Once both the methods returns their respective TResults we can compare accordingly and proceed with the redirection.
Now I guess we have gone through a basic example and have some basic idea of what asynchronous programming is and how it can serve us to boost performance and UI responsiveness.

Points to Remember about Asynchronous Programming.


  • The Asynchronous method must have async modifier
  • The async method must have at least one await expression.
  • The return type is of Task or void.
Happy codding...

No comments:

Post a Comment