Отправить асинхронные электронные письма
Я использую ASP.NET MVC 3 с MVCMailer, я пытался отправить электронную почту с помощью SendAsync, но на самом деле это все еще занимает больше времени.
Поэтому я пытаюсь использовать Task.Factory, как показано ниже:
var task1 = Task.Factory.StartNew(
state =>
{
var mail = new UserMailer();
var msg = mail.Welcome("My Name", "myemail@gmail.com");
msg.SendAsync();
});
task1.Wait();
Проблема в том, что MVCMailer нужен HttpContext, но внутри этой задачи я получил HttpContext Null.
Как я могу отправлять электронные письма Async?
4 ответа
Небольшое дополнение к этому. Вот метод расширения, чтобы помочь некоторым.
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();
});
}
Используйте его, как следует из методов вашего контроллера.
Mailers.UserMailer um = new Mailers.UserMailer();
um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context);
Task.Factory.StartNew
создаст новую тему.
Если вы хотите получить доступ к HttpContext, который находится в главном потоке, вы должны сделать это:
var task1 = Task.Factory.StartNew(() =>
{
System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context;
var mail = new UserMailer();
var msg = mail.Welcome("My Name", "myemail@gmail.com");
msg.SendAsync();
});
task1.Wait();
Там долго спорят, лучше ли использовать TPL или QueueUserWorkItem.
Кто-то пытался решить проблему.
Это версия QueueUserWorkItem:
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; }
}
}
Вам не нужны задачи. SendAsync является асинхронным и использует другой поток самостоятельно. Задачи не ускоряют вашу рассылку.
ОБНОВЛЕНИЕ: Когда я решаю ту же проблему, я использую задачу и синхронную отправку. Похоже, что SendAsync не был таким асинхронным. Это пример моего кода (не хочу HttpContext):
public void SendMailCollection(IEnumerable<Tuple<string, string, MailAddress>> mailParams)
{
var smtpClient = new SmtpClient
{
Credentials = new NetworkCredential(_configurationService.SmtpUser, _configurationService.SmtpPassword),
Host = _configurationService.SmtpHost,
Port = _configurationService.SmtpPort.Value
};
var task = new Task(() =>
{
foreach (MailMessage message in mailParams.Select(FormMessage))
{
smtpClient.Send(message);
}
});
task.Start();
}
private MailMessage FormMessage(Tuple<string, string, MailAddress> firstMail)
{
var message = new MailMessage
{
From = new MailAddress(_configurationService.SmtpSenderEmail, _configurationService.SmtpSenderName),
Subject = firstMail.Item1,
Body = firstMail.Item2
};
message.To.Add(firstMail.Item3);
return message;
}
public class UserMailer : MailerBase
{
RegisterService Service = new RegisterService();
HttpContext Context;
public UserMailer(HttpContext context)
{
Context = context;
MasterName="_Layout";
}
public void ConfirmRegistration(Register model)
{
SendAsync(() =>
{
model.Conference = Service.Context.Conferences.Find(model.ConferenceID); // load conference bcs it fails to lazy load automatically.
ViewData.Model = model;
return Populate(x =>
{
x.Subject = "You registered for " + model.Conference.Name + "!"; ;
x.ViewName = "Confirm";
x.To.Add(model.Email);
});
});
}
private void SendAsync(Func<MvcMailMessage> GetEmail)
{
Task.Factory.StartNew(() =>
{
System.Web.HttpContext.Current = Context;
GetEmail().Send();
});
}