asp.net mvc 3 - example - SmtpClient.SendAsync bloqueando mi solicitud ASP.NET MVC
enviar email mvc c# (4)
Tengo una acción que envía un correo electrónico simple:
[HttpPost, ActionName("Index")]
public ActionResult IndexPost(ContactForm contactForm)
{
if (ModelState.IsValid)
{
new EmailService().SendAsync(contactForm.Email, contactForm.Name, contactForm.Subject, contactForm.Body, true);
return RedirectToAction(MVC.Contact.Success());
}
return View(contactForm);
}
Y un servicio de correo electrónico:
public void SendAsync(string fromEmail, string fromName, string subject, string body, bool isBodyHtml)
{
MailMessage mailMessage....
....
SmtpClient client = new SmtpClient(settingRepository.SmtpAddress, settingRepository.SmtpPort);
client.EnableSsl = settingRepository.SmtpSsl;
client.Credentials = new NetworkCredential(settingRepository.SmtpUserName, settingRepository.SmtpPassword);
client.SendCompleted += client_SendCompleted;
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage));
}
private void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
Tuple<SmtpClient, MailMessage> data = (Tuple<SmtpClient, MailMessage>)e.UserState;
data.Item1.Dispose();
data.Item2.Dispose();
if (e.Error != null)
{
}
}
Cuando envío un correo electrónico, estoy usando el método Async, luego mi método SendAsync regresa inmediatamente, luego se llama a RedirectToAction. Pero la respuesta (en este caso, una redirección) no es enviada por ASP.NET hasta que se complete client_SendCompleted.
Esto es lo que trato de entender:
Al ver la ejecución en el depurador de Visual Studio, SendAsync vuelve inmediatamente (y se llama a RedirectToAction), pero no sucede nada en el navegador hasta que se envía el correo electrónico.
Si pongo un punto de interrupción dentro de client_SendCompleted, el cliente se queda cargando ... hasta que presiono F5 en el depurador.
Con .Net 4.5.2 , puede hacer esto con ActionMailer.Net
:
var mailer = new MailController();
var msg = mailer.SomeMailAction(recipient);
var tcs = new TaskCompletionSource<MailMessage>();
mailer.OnMailSentCallback = tcs.SetResult;
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
msg.DeliverAsync();
await tcs.Task;
Trace.TraceInformation("Mail sent to " + recipient);
});
Lea esto primero: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
Envié el error a Microsoft Connect connect.microsoft.com/VisualStudio/feedback/details/688210/…
Este es uno interesante. He reproducido el comportamiento inesperado, pero no puedo explicarlo. Seguiré cavando
De todos modos, la solución parece ser poner en cola un hilo de fondo, lo que de SendAsync
el propósito de usar SendAsync
. Terminas con esto:
MailMessage mailMessage = new MailMessage(...);
SmtpClient client = new SmtpClient(...);
client.SendCompleted += (s, e) =>
{
client.Dispose();
mailMessage.Dispose();
};
ThreadPool.QueueUserWorkItem(o =>
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage)));
Que bien puede ser:
ThreadPool.QueueUserWorkItem(o => {
using (SmtpClient client = new SmtpClient(...))
{
using (MailMessage mailMessage = new MailMessage(...))
{
client.Send(mailMessage, Tuple.Create(client, mailMessage));
}
}
});
Esto es por diseño. ASP.NET esperará automáticamente a que finalice la solicitud cualquier trabajo asincrónico pendiente si se inició el trabajo asincrónico de una manera que llame al SynchronizationContext subyacente. Esto es para asegurarse de que si su operación asincrónica intenta interactuar con HttpContext , HttpResponse , etc., seguirá existiendo.
Si quieres hacer fuego verdadero y olvidarlo, debes envolver tu llamada en ThreadPool.QueueUserWorkItem
. Esto obligará a que se ejecute en un nuevo subproceso de grupo de subprocesos sin pasar por el SynchronizationContext , por lo que la solicitud volverá felizmente.
Sin embargo, tenga en cuenta que si por algún motivo el dominio de la aplicación se desactiva mientras su envío aún está en progreso (por ejemplo, si cambió el archivo web.config, dejó caer un archivo nuevo en el bin, recicló el grupo de aplicaciones, etc.) su asincronización enviar sería abruptamente interrumpido. Si te importa eso, echa un vistazo a Phil Haacks WebBackgrounder para ASP.NET , que te permite hacer cola y ejecutar trabajos de fondo (como enviar un correo electrónico) de tal manera que se asegure que finalice con elegancia en caso de que el dominio de la aplicación se apague .