3

If I use Send() with MVCMailer, my SES works fine, but SendAsync() shows the error message below, does anyone know of a work around for this? THanks!

System.Net.Mail.SmtpException: Failure sending mail. ---> System.InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>.
   at System.Web.AspNetSynchronizationContext.OperationStarted()
   at System.ComponentModel.AsyncOperation.CreateOperation(Object userSuppliedState, SynchronizationContext syncContext)
   at System.Net.Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
   --- End of inner exception stack trace ---
   at System.Net.Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
   at Mvc.Mailer.SmtpClientWrapper.SendAsync(MailMessage mailMessage, Object userState)
   at Mvc.Mailer.MailMessageExtensions.SendAsync(MailMessage message, Object userState, ISmtpClient smtpClient)
   at MVCWebsite.Helpers.AccountHelper.RegisterNewUser(BaseDBContext db, AccountViewModelForReg VM, Boolean isCaptchaValid, Boolean modelValidity) in c:\Users\William-Business\Desktop\TWB\TWB Central\Projects\AwesomeSauce\AwesomeSauce\Helpers\AccountHelper.cs:line 316
   at MVCWebsite.Controllers.AccountController.Register(AccountViewModelForReg VM, Boolean captchaValid) in c:\Users\William-Business\Desktop\TWB\TWB Central\Projects\AwesomeSauce\AwesomeSauce\Controllers\AccountController.cs:line 308
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<>c__DisplayClass2a.<BeginInvokeAction>b__20()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult)
williamsandonz
  • 15,864
  • 23
  • 100
  • 186
  • Did you ever get a resolution to this? I just ran into this same thing. – Matt Mar 03 '13 at 03:48
  • It turns out this isn't an issue with MVCMailer but rather some threading stuff with MVC. Take a look at my answer in this thread: http://stackoverflow.com/a/15182352/969488 – Matt Mar 03 '13 at 04:08
  • @Baconbeastnz can you comment on why you approved my answer? – JJS Mar 06 '13 at 21:47

1 Answers1

5

This error is by design. You can turn this off globally:

<appSettings>
  <add key="aspnet:AllowAsyncDuringSyncStages" value="false" />
</appSettings>

reference http://msdn.microsoft.com/en-us/library/hh975440.aspx

Something else to consider is using a Task to do the async in the background, decoupling it from the scheduler that ASP.Net uses. Using this method, you wouldn't need to change the appSetting.

using Mvc.Mailer;
...
public ActionResult SendWelcomeMessage()
{
    Task.Factory.StartNew(() => UserMailer.Welcome().SendAsync());
    return RedirectToAction("Index");
}

Edit

Enabling AllowAsyncDuringSyncStages or using the Task Parallel Library both have potential drawbacks. Using AsyncController does not have either drawback. Thanks @StephenCleary for challenging my answer.

public class HomeController : AsyncController
{
  public void SendMessageAsync()
  {
    var client = new SmtpClientWrapper();
    client.SendCompleted += (sender, args) => 
      AsyncManager.OutstandingOperations.Decrement();
    AsyncManager.OutstandingOperations.Increment();
    new UserMailer().Welcome().SendAsync("", client);
  }

  public ActionResult SendMessageCompleted()
  {
    return View();
  }
}
niico
  • 11,206
  • 23
  • 78
  • 161
JJS
  • 6,431
  • 1
  • 54
  • 70
  • Related question: Is this setting dangerous? http://stackoverflow.com/questions/16469094/starting-and-forgetting-an-async-task-in-mvc-action – Chris Moschini May 09 '13 at 19:18
  • Yes, the setting is dangerous. And I would recommend `Send` over `StartNew` wrapping `SendAsync`. – Stephen Cleary Dec 05 '13 at 05:18
  • @StephenCleary are you recommending StartNew(() => ...Send()) vs StartNew(() => ...SendAsync())? – JJS Dec 05 '13 at 18:08
  • @JJS: No, I'm recommending `Send` over `StartNew(() => ...SendAsync())`. – Stephen Cleary Dec 05 '13 at 18:54
  • @StephenCleary why do you recommend a straignt sync Send call over either form of async using StartNew(...)? – JJS Dec 05 '13 at 19:05
  • @JJS: `StartNew` kicks off an in-memory operation that ASP.NET and IIS are not aware of. So when the normal AppDomain/application pool recycling kicks in, you might very well mysteriously lose some emails. That's the point of the `InvalidOperationException`, which is an error, not a warning. Turning it off is just deciding to live really dangerously IMO. – Stephen Cleary Dec 05 '13 at 19:44
  • @StephenCleary thank you for explaining your concern. I agree that loosing using the TPL creates a Thread that IIS will not be concerned with because of how SmtpClient.SendAsync uses the SynchronizationContext described in this question. http://stackoverflow.com/questions/10250042/does-mvcmailer-sendasync-block-a-asp-net-mvc-request. I should have suggested an answer using AsyncController/AsyncManager instead. I'll make a note to work up a solution and modify my answer. – JJS Dec 05 '13 at 20:40