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?
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.
As of .NET 4.5 SmtpClient implements async awaitable method SendMailAsync. As a result, to send email asynchronously is as following:
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.
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.
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);
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; }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With