mail async c# asp.net-mvc

c# - .SendMailAsync() utiliza en MVC



send email asynchronous (3)

Creo que lo siguiente hace lo que estás tratando de lograr:

Modifique su controlador de la siguiente manera:

public async Task<ActionResult> Index() { Email email = new Email(); email.SendAsync(); }

Y en su clase de correo electrónico agregue el método SendAsync como se muestra a continuación

public async Task SendAsync() { await Task.Run(() => this.send()); }

La acción volverá antes de que se envíe el correo electrónico y la solicitud no se bloqueará.

Intente ver el comportamiento con la plantilla mvc predeterminada con este código:

[AllowAnonymous] public ActionResult Login(string returnUrl) { LongRunningTaskAsync(); return View(); } public static async Task LongRunningTaskAsync() { await Task.Run(() => LongRunningTask()); } public static void LongRunningTask() { Debug.WriteLine("LongRunningTask started"); Thread.Sleep(10000); Debug.WriteLine("LongRunningTask completed"); }

La página de inicio de sesión se mostrará al instante. Pero la ventana de salida mostrará "LongRunningTask completed" 10 segundos después.

El tiempo total de entrega puede ser más largo con este método como se menciona en los comentarios, pero creo que el OP estaba intentando devolver la acción inmediatamente y enviar el correo electrónico en segundo plano.

Estoy intentando enviar un correo electrónico desde mi aplicación MVC, se envía bien cuando uso el método .Send (), pero tarda un tiempo en volver, así que quise usar la función .SendMailAsync (), pero recibo el siguiente error durante ejecución.

No se puede iniciar una operación asíncrona en este momento. Las operaciones asíncronas solo pueden iniciarse dentro de un controlador o módulo asíncrono o durante ciertos eventos en el ciclo de vida de la página. Si se produjo esta excepción al ejecutar una página, asegúrese de que la página esté marcada <% @ Page Async = "true"%>

Este es mi ejemplo de código. ¿Cómo puedo configurar esto para enviar usando .SendMailAsync ()

Clase de envoltorio de correo electrónico:

using System.Net.Mail; namespace Helpers { public class Email { // constants private const string HtmlEmailHeader = "<html><head><title></title></head><body style=''font-family:arial; font-size:14px;''>"; private const string HtmlEmailFooter = "</body></html>"; // properties public List<string> To { get; set; } public List<string> CC { get; set; } public List<string> BCC { get; set; } public string From { get; set; } public string Subject { get; set; } public string Body { get; set; } // constructor public Email() { To = new List<string>(); CC = new List<string>(); BCC = new List<string>(); } // send public void Send() { MailMessage message = new MailMessage(); foreach (var x in To) { message.To.Add(x); } foreach (var x in CC) { message.CC.Add(x); } foreach (var x in BCC) { message.Bcc.Add(x); } message.Subject = Subject; message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter); message.BodyEncoding = System.Text.Encoding.UTF8; message.From = new MailAddress(From); message.SubjectEncoding = System.Text.Encoding.UTF8; message.IsBodyHtml = true; SmtpClient client = new SmtpClient("relay.mail.server"); client.SendMailAsync(message); } } }

Controlador:

public ActionResult Index() { Email email = new Email(); email.To.Add("[email protected]"); email.From = "[email protected]"; email.Subject = "Subject"; email.Body = "<p><strong>Hello</strong></p><p>This is my first Email Message</p>"; email.Send(); }

EDITAR

Además de la pregunta real, el problema subyacente fue el retraso creado al enviar correos electrónicos. Miré más a fondo el problema real y con la ayuda de este post:

ASP.Net MVC hilos de fondo para la creación y el envío de correo electrónico

modifiqué mi clase de Email Wrapper para generar un nuevo hilo para realizar el procesamiento del correo electrónico:

using System.Net.Mail; namespace Helpers { public class Email { // constants private const string HtmlEmailHeader = "<html><head><title></title></head><body style=''font-family:arial; font-size:14px;''>"; private const string HtmlEmailFooter = "</body></html>"; // properties public List<string> To { get; set; } public List<string> CC { get; set; } public List<string> BCC { get; set; } public string From { get; set; } public string Subject { get; set; } public string Body { get; set; } // constructor public Email() { To = new List<string>(); CC = new List<string>(); BCC = new List<string>(); } // send public void Send() { MailMessage message = new MailMessage(); foreach (var x in To) { message.To.Add(x); } foreach (var x in CC) { message.CC.Add(x); } foreach (var x in BCC) { message.Bcc.Add(x); } message.Subject = Subject; message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter); message.BodyEncoding = System.Text.Encoding.UTF8; message.From = new MailAddress(From); message.SubjectEncoding = System.Text.Encoding.UTF8; message.IsBodyHtml = true; SmtpClient client = new SmtpClient("relay.mail.server"); new Thread(() => { client.Send(message); }).Start(); } } }


Es cierto que el error es un poco obtuso, pero todo lo que realmente te dice es que estás llamando a un método asíncrono desde un método síncrono, lo cual no está permitido. Si va a usar async, tiene que usar async en toda la cadena.

Entonces, primero debe cambiar la definición de su método de Send para devolver una Task :

public async Task Send()

Y configura la llamada a tu método asíncrono para esperar:

await client.SendMailAsync(message);

Luego, haz lo mismo por tu acción:

public async Task<ActionResult> Index()

Y:

await email.Send();

ACTUALIZAR

Async no hace lo que creo que crees que hace. Cuando su acción es invocada por una solicitud, no devolverá una respuesta hasta que todo el código dentro de la acción se haya ejecutado completamente. Async no es una varita mágica que hace que la acción devuelva la respuesta más rápido. Su tarea (en este caso, el envío de un correo electrónico) toma todo el tiempo necesario y asíncrono o no, la acción no devolverá una respuesta hasta que la tarea haya finalizado.

Entonces, ¿por qué usar async entonces? Porque lo que hace async es soltar el hilo del grupo de servidores. Digamos que IIS se está ejecutando en una configuración bastante estándar, es probable que tengas alrededor de 1000 subprocesos disponibles. Esto suele denominarse "solicitudes máximas", porque normalmente 1 solicitud == 1 subproceso. Por lo tanto, si su servidor tiene una carga pesada y está enviando más que las "solicitudes máximas", cada solicitud subsiguiente se pone en cola hasta que un subproceso del grupo vuelva a estar disponible. Si todos los subprocesos están atados esperando a que algo se complete, entonces su servidor es esencialmente un punto muerto. Pero, cuando usas async, le dices a IIS esencialmente: "Estoy esperando algo. Aquí está mi hilo, así que puedes usarlo para responder otra solicitud. Te avisaré cuando lo necesite". Eso permite que las solicitudes en la cola continúen.

Largo y corto, use siempre async cuando esté haciendo algo que implique esperar, ya que permite que los recursos del servidor se utilicen de manera más eficiente, pero recuerde que no hace que las cosas sucedan más rápido.

EDITAR 12/11/14 - Se actualizó un poco la terminología para aclarar que async solo es útil cuando un subproceso está esperando , no solo involucrado en alguna tarea de larga ejecución. Por ejemplo, ejecutar cálculos financieros complejos podría "tomar un tiempo", pero no sería una buena opción para async porque todo el trabajo está vinculado a la CPU. La tarea puede ser de larga ejecución, pero si el subproceso no está en estado de espera, no se puede usar para otras tareas y su método asíncrono se ejecutará simplemente como sincronización, pero con sobrecarga adicional.


Esto podría ayudarte.

public void Send(MailAddress toAddress, string subject, string body, bool priority) { Task.Factory.StartNew(() => SendEmail(toAddress, subject, body, priority), TaskCreationOptions.LongRunning); } private void SendEmail(MailAddress toAddress, string subject, string body, bool priority) { MailAddress fromAddress = new MailAddress(WebConfigurationManager.AppSettings["SmtpFromAddress"]); string serverName = WebConfigurationManager.AppSettings["SmtpServerName"]; int port = Convert.ToInt32(WebConfigurationManager.AppSettings["SmtpPort"]); string userName = WebConfigurationManager.AppSettings["SmtpUserName"]; string password = WebConfigurationManager.AppSettings["SmtpPassword"]; var message = new MailMessage(fromAddress, toAddress); message.Subject = subject; message.Body = body; message.IsBodyHtml = true; message.HeadersEncoding = Encoding.UTF8; message.SubjectEncoding = Encoding.UTF8; message.BodyEncoding = Encoding.UTF8; if (priority) message.Priority = MailPriority.High; Thread.Sleep(1000); SmtpClient client = new SmtpClient(serverName, port); client.DeliveryMethod = SmtpDeliveryMethod.Network; client.EnableSsl = Convert.ToBoolean(WebConfigurationManager.AppSettings["SmtpSsl"]); client.UseDefaultCredentials = false; NetworkCredential smtpUserInfo = new NetworkCredential(userName, password); client.Credentials = smtpUserInfo; client.Send(message); client.Dispose(); message.Dispose(); }

Thread.Sleep está allí porque esto enviará el correo tan rápido que muchos servidores SMTP informarán demasiados correos electrónicos desde el mismo mensaje de error de IP. Aunque ASP.NET maneja el envío de correo asíncrono, no enviará más de un mensaje a la vez. Espera hasta que ocurra la devolución de llamada antes de enviar otro correo electrónico. Este enfoque enviará mensajes en paralelo tan rápido como el código pueda llamar a Enviar ().