Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send async e-mails

I am using ASP.NET MVC 3 with MVCMailer, I tried to send e-mails using SendAsync, but actually it still take longer.

So I am trying to use Task.Factory like the code bellow:

   var task1 = Task.Factory.StartNew(
            state =>
            {
                var mail = new UserMailer();
                var msg = mail.Welcome("My Name", "[email protected]");
                msg.SendAsync(); 
            });

   task1.Wait();

The problem is, MVCMailer needs HttpContext, but inside this task I got HttpContext Null.

How can I send Async e-mails?

like image 697
Michel Andrade Avatar asked Aug 30 '12 16:08

Michel Andrade


People also ask

What is send async async mail message?

SendAsync (MailMessage, Object) Sends the specified email message to an SMTP server for delivery. This method does not block the calling thread and allows the caller to pass an object to the method that is invoked when the operation completes.

Is it possible to send email asynchronously using smtpclient?

As of .NET 4.5 SmtpClient implements async awaitable method SendMailAsync. As a result, to send email asynchronously is as following:

How do I cancel a sendasync operation?

You can cancel a SendAsync operation by calling the SendAsyncCancel method. After calling SendAsync, you must wait for the email transmission to complete before attempting to send another email message using Send or SendAsync.

Is it possible to use sendasync inside a thread?

As for using SendAsync inside the thread. The benefit of using this is you get the callback and from there you can determine the result of the send. If we wanted to use the Send method instead we would need to wrap this in a try...catch block incase it failed.


2 Answers

A small addition to this. Here is an extension method to help some.

using Mvc.Mailer; 
using System.Threading.Tasks;
public static void SendEmailAsync(this MvcMailMessage msg, HttpContext currContext)
{
      //make this process a little cleaner
      Task.Factory.StartNew(() =>
      {
           System.Web.HttpContext.Current = currContext;
           msg.SendAsync();
       });
}

Use it like follows from your controller methods.

Mailers.UserMailer um = new Mailers.UserMailer();
um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context);
like image 199
Matt Avatar answered Nov 15 '22 23:11

Matt


Task.Factory.StartNew will create a new thread.
If you want to access HttpContext which is in the main thread you have to do this:

var task1 = Task.Factory.StartNew(() =>
    {
    System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context;
    var mail = new UserMailer();
    var msg = mail.Welcome("My Name", "[email protected]");
    msg.SendAsync();
    });

task1.Wait();

There a long debate if it's better to use TPL or QueueUserWorkItem.
Someone has tried to address the issue.
This is the QueueUserWorkItem version:

public class HomeController : Controller
{
    private AutoResetEvent s_reset = new AutoResetEvent(false);

    public ActionResult Index()
    {
        var state = new WorkerState() { HttpContextReference = System.Web.HttpContext.Current };
        ThreadPool.QueueUserWorkItem(new WaitCallback(EmaiSenderWorker), state);

        try
        {
        s_reset.WaitOne();
        }
        finally
        {
        s_reset.Close();
        }

        return View();
    }

    void EmaiSenderWorker(object state)
    {
        var mystate = state as WorkerState;

        if (mystate != null && mystate.HttpContextReference != null)
        {
        System.Web.HttpContext.Current = mystate.HttpContextReference;
        }

        var mail = new UserMailer();
        var msg = mail.Welcome();
        msg.SendAsync();

        s_reset.Set();
    }

    private class WorkerState
    {
        public HttpContext HttpContextReference { get; set; }
    }

}
like image 23
LeftyX Avatar answered Nov 15 '22 21:11

LeftyX