Wednesday, January 25, 2017

ASP.NET MVC with Async Action

Leave a Comment

I need to send an asynchronous email from an Async action. I do not understand why the following error is happening, being that I use this same class in other projects and use the same form only without errors, everything quiet.

Error:

The asynchronous action method 'EsqueciMinhaSenhaAsync' returns a Task, which cannot be executed synchronously.

Action:

        [AllowAnonymous]         [HttpPost, ValidateAntiForgeryToken]         public async Task<ActionResult> EsqueciMinhaSenhaAsync(UsuarioEsqueciMinhaSenhaViewModel vModel)         {             if (ModelState.IsValid)             {                 var conteudo = "este é o conteudo do email";                 var nomeRemetente = "esse é o nome do remetente";                  if(await EmailService.SendAsync(Language.PasswordRecovery, conteudo, vModel.EmailOuUsername, nomeRemetente))                 {                     TempData["MensagemRetorno"] = Language.EmailSendedWithSuccess;                     return View("login");                 }             }              TempData["MensagemRetorno"] = Language.ErrorSendingEmail;             return View("EsqueciMinhaSenha");         } 

My Email Service:

    public static async Task<bool> SendAsync(string assunto, string conteudo, string destinatario, string nomeDestinatario)     {         // Habilitar o envio de e-mail         var appSetting = ConfigurationManager.AppSettings;          if (appSetting != null && appSetting.Count >= 7 && !string.IsNullOrEmpty(assunto) && !string.IsNullOrEmpty(conteudo) && !string.IsNullOrEmpty(destinatario) && !string.IsNullOrEmpty(nomeDestinatario))         {             int port = 0;             bool useSSl = false;              using (var msg = new MailMessage             {                 From = new MailAddress(appSetting["EmailFrom"], appSetting["EmailNameFrom"]),                 Body = WebUtility.HtmlEncode(conteudo)             })             {                 int.TryParse(appSetting["EmailPort"], out port);                 bool.TryParse(appSetting["EmailUseSSL"], out useSSl);                   msg.ReplyToList.Add(destinatario);                 msg.To.Add(new MailAddress(destinatario, nomeDestinatario));                 msg.Subject = assunto;                 msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Plain));                 msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Html));                  using (var smtpClient = new SmtpClient(appSetting["EmailServer"], port))                 {                     var credentials = new NetworkCredential(appSetting["EmailUserName"], appSetting["EmailPassword"]);                     smtpClient.Credentials = credentials;                     smtpClient.EnableSsl = useSSl;                     await smtpClient.SendMailAsync(msg);                      return await Task.FromResult(true);                 }             }         }          return await Task.FromResult(false);     } 

2 Answers

Answers 1

I was having same sort of issue, and when I made all possible path awaitable then issue resolved.

Please make changes to your action EsqueciMinhaSenhaAsync

currently your last line is:

return View("EsqueciMinhaSenha");

change it to

//It looks async/wait pattern expects all paths to have awaitable if async is used

return await View("EsqueciMinhaSenha");

Answers 2

1) Why don't you add sync API version based on SmtpClient.SendMail? https://msdn.microsoft.com/en-us/library/swas0fwc(v=vs.110).aspx

public static bool SendSync(string assunto, string conteudo, string destinatario, string nomeDestinatario) {     // Habilitar o envio de e-mail     var appSetting = ConfigurationManager.AppSettings;      if (appSetting != null && appSetting.Count >= 7 && !string.IsNullOrEmpty(assunto) && !string.IsNullOrEmpty(conteudo) && !string.IsNullOrEmpty(destinatario) && !string.IsNullOrEmpty(nomeDestinatario))     {         int port = 0;         bool useSSl = false;          using (var msg = new MailMessage         {             From = new MailAddress(appSetting["EmailFrom"], appSetting["EmailNameFrom"]),             Body = WebUtility.HtmlEncode(conteudo)         })         {             int.TryParse(appSetting["EmailPort"], out port);             bool.TryParse(appSetting["EmailUseSSL"], out useSSl);               msg.ReplyToList.Add(destinatario);             msg.To.Add(new MailAddress(destinatario, nomeDestinatario));             msg.Subject = assunto;             msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Plain));             msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Html));              using (var smtpClient = new SmtpClient(appSetting["EmailServer"], port))             {                 var credentials = new NetworkCredential(appSetting["EmailUserName"], appSetting["EmailPassword"]);                 smtpClient.Credentials = credentials;                 smtpClient.EnableSsl = useSSl;                 smtpClient.SendMail(msg);                  return true;             }         }     }      return false; } 

2) Another option is to use IAsyncOperation as a returning result of your email service. That way, you can use this as both async way (via await) and sync way like that:

var asyncOp = EmailService.SendAsync(some-params-here); var task = asyncOp.AsTask(); task.Wait(); return task.Result; // TODO it's nice to care about exceptions too! 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment