Prevent the system from crashing during a batch email

4

I'm developing a system that sends batch email to clients, but if the user who is operating the system clicks on any part of the window during the process, it appears that the system is not responding, interrupting the sending process .

I would like a solution so that even the user clicking on the screen or performing operations on other programs the system will continue sending the emails and informing the percentage completed.

private void bnt_EnviarEmail_Click(object sender, EventArgs e) {
    for (int i = 0; i < grid1.RowCount; i++) {
        if (grid1.Rows[i].Cells[5].Value.ToString() == "S") //VERIFICA SE E O ENVIO DE EMAIL ESTA ATIVO PARA ESTE CLIENTE
        {
            int COD_CLI_atual = int.Parse(grid1.Rows[i].Cells[0].Value.ToString());
            using(DatabaseEntities db = new DatabaseEntities()) {
                Clientes c = (from cli in db.Clientes where cli.CODIGO == COD_CLI_atual select cli).FirstOrDefault();
                string email = c.EMAIL;
                string nome_fantasia = c.NOME_FANTASIA;
                string cnpj = c.CNPJ;

                EnviarEmail em = new EnviarEmail();
                em.Enviar(email, "MENSAGEM DO EMAIL" + nome_fantasia + " " + cnpj);
            }
        }
    }
}

Method used to send email:

public void Enviar(string Destinatario, string Assunto, string Texto) {
    string Usuario = usuario;
    string Senha = senha;
    int porta = 0;
    bool ssl;
    string servidor = true;
    Email = new MailMessage();
    Email.To.Add(new MailAddress(Destinatario));
    Email.From = new MailAddress(Usuario);
    Email.Subject = Assunto;
    Email.IsBodyHtml = false;
    Email.Body = Texto;
    SmtpClient cliente = new SmtpClient(servidor, porta);
    cliente.Credentials = new System.Net.NetworkCredential(Usuario, Senha);
    cliente.EnableSsl = true;
    cliente.Send(Email);
}
    
asked by anonymous 21.01.2015 / 21:25

3 answers

4

The basic code for sending e-mail messages asynchronously is this:

public async Task EnviarAsync(string Destinatario, string Assunto, string Texto)
{
    string Usuario = usuario;
    string Senha = senha;
    int porta = 0;
    bool ssl;
    string servidor = true;
    Email = new MailMessage();
    Email.To.Add(new MailAddress(Destinatario));
    Email.From = new MailAddress(Usuario);
    Email.Subject = Assunto;
    Email.IsBodyHtml = false;
    Email.Body = Texto;
    SmtpClient cliente = new SmtpClient(servidor, porta);
    cliente.Credentials = new System.Net.NetworkCredential(Usuario, Senha);
    cliente.EnableSsl = true;
    await cliente.SendAsync(Email);    
 }

Your code has other issues such as potential resource leakage, it does not address issues with the submission, but the solution is basically this.

Here you call it:

await em.EnviarAsync(email, "MENSAGEM DO EMAIL" + nome_fantasia + " " + cnpj);

Note that you can have both methods, one asynchronous and the other synchronous, and choose the most appropriate one at a time. The Async suffix in the name is part of the default setting.

In this way the code starts the execution of the task but also does not continue running the normal flow, it does not wait for the execution of the method to end.

To learn more, see the documentation .

Documentation of SendAsync .

You already have a question about the subject .

More examples of using async .

Answer in SO with a more complete example.

    
21.01.2015 / 22:15
1

It would let it be asynchronous (not at the same time). Use the ELMAH.MVC error report, and try this example:

public void SendThat(MailMessage message)
{
    AsyncMethodCaller caller = new AsyncMethodCaller(SendMailInSeperateThread);
    AsyncCallback callbackHandler = new AsyncCallback(AsyncCallback);
    caller.BeginInvoke(message, callbackHandler, null);
}

private delegate void AsyncMethodCaller(MailMessage message);

private void SendMailInSeperateThread(MailMessage message)
{
    try
    {
        SmtpClient client = new SmtpClient();
        client.Timeout = 20000; // 20 second timeout... why more?
        client.Send(message);
        client.Dispose();
        message.Dispose();

        // If you have a flag checking to see if an email was sent, set it here
        // Pass more parameters in the delegate if you need to...
    }
    catch (Exception e)
    {
         // This is very necessary to catch errors since we are in
         // a different context & thread
         Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
    }
}

private void AsyncCallback(IAsyncResult ar)
{
    try
    {
        AsyncResult result = (AsyncResult)ar;
        AsyncMethodCaller caller = (AsyncMethodCaller)result.AsyncDelegate;
        caller.EndInvoke(ar);
    }
    catch (Exception e)
    {
        Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
        Elmah.ErrorLog.GetDefault(null).Log(new Error(new Exception("Emailer - This hacky asynccallback thing is puking, serves you right.")));
    }
}
    
21.01.2015 / 21:45
0

A good solution would be to use a webservice to send the emails, you just need to put the emails in a persistent queue and the sending service would do the job without haste and without placing load on the application server, you can use a ready service like mailgun or the SES Amazon or create your own shipping service that is not so complicated.

    
22.01.2015 / 03:38