8

I have the following action method with async and await keywords:

[HttpPost]
public async Task<ActionResult> Save(ContactFormViewModel contactFormVM)
{
     if (domain.SaveContactForm(contactFormVM) > 0)// saves data in database
     {
         bool result = await SendMails(contactFormVM);//need to execute this method asynchronously but it executes synchronously
         return Json("success");
     }
         return Json("failure");
  }

    public async Task<bool> SendMails(ContactFormViewModel contactFormVM)
    {
            await Task.Delay(0);//how to use await keyword in this function?
            domain.SendContactFormUserMail(contactFormVM);
            domain.SendContactFormAdminMail(contactFormVM);
            return true;
    }

In the above code, once the database operation is finished I want to immediately return Json() result and then call the SendMails() method which should execute in the background. What changes should I make to the above code?

botero
  • 598
  • 2
  • 11
  • 23
seadrag0n
  • 848
  • 1
  • 16
  • 40
  • What error are you getting? – Tharif Apr 01 '15 at 04:59
  • @utility I am not getting any error. The `SendMails()` method is executing synchronously instead of executing asynchronously. The `return Json("success")` should be called on the current thread and `SendMails()` should execute asynchronously.. – seadrag0n Apr 01 '15 at 05:02

1 Answers1

13

The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes. The task represents ongoing work.

It sounds like you don't want to wait for the result of SendMails. Think of async and await as tools to consume asynchronous APIs. Specifically, it's really useful to be able to "await" for the result of an "async" task. However, if you don't care about the result of your "async" (e.g. SendMails) task, then you don't need to "await" for the result (i.e. boolean).

Instead, you can simply use Task.Run to invoke your asynchronous task.

[HttpPost]
public async Task<ActionResult> Save(ContactFormViewModel contactFormVM) {
  if (domain.SaveContactForm(contactFormVM) > 0) {// saves data in database 
    Task.Run(() => SendMails(contactFormVM));
    return Json("success");
  }
  return Json("failure");
}

public void SendMails(ContactFormViewModel contactFormVM) {
  domain.SendContactFormUserMail(contactFormVM);
  domain.SendContactFormAdminMail(contactFormVM);
}
Steven Wexler
  • 16,589
  • 8
  • 53
  • 80
  • this is exactly what I need and it seems to be working, but in the `SendContactFormUserMail()` function I am using `StreamReader` class to read the email template from a text file but I am getting `Object reference not set to an instance of an object.` exception.. – seadrag0n Apr 01 '15 at 05:52
  • It sounds like one of your variables is `null`. Answers to this question may help: http://stackoverflow.com/questions/779091/what-does-object-reference-not-set-to-an-instance-of-an-object-mean – Steven Wexler Apr 01 '15 at 05:56
  • 1
    I am using `HttpContext.Current.Server.MapPath()` method to read a text file and the value for this is null in the async method... – seadrag0n Apr 01 '15 at 06:09
  • 1
    Your HttpContext.Current is null because invoking asynchronous tasks will use a worker thread which does not have access to HttpContext.Current. There are a few questions that may help you. This is an example of a question/answer related to this problem: http://stackoverflow.com/questions/19111218/httpcontext-current-null-inside-async-task – Steven Wexler Apr 01 '15 at 06:13
  • solved my issue using `HostingEnvironment.ApplicationPhysicalPath;` to get the application physical path and tested the code on server, working really fast now..thanks for the simple answer... – seadrag0n Apr 01 '15 at 06:35