Prevent the system from locking during a batch e-mail

Asked

Viewed 325 times

6

I am developing a system that sends mail in batches to customers, but if the user who is operating the system click 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 keeps sending the emails and reporting the completed percentage.

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);
}
  • Excerpt from the code posted.

  • Ideally this class EnviarEmail is that it would need to be rewritten to use asynchronicity. Inside it you are using SmtpClient? Use the method Send? Tried to change to SendAsync?

  • I edited the post and put the code of the Send email class. I use the Smtpclient method. How Sendasync works?

  • I would do one of the following ways: using Application.Doevents or using the component Backgroundworker, I will not answer officially because I believe there are more ideal solutions.

3 answers

5


The basic code for sending email 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);    
 }

I put in the Github for future reference.

Your code has other problems like potential leak feature, it does not deal with sending problems, but the solution basically is this.

That’s what you call it:

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

Note that you can have both methods, one asynchronous and one synchronous and choose which one is most suitable at each time. The suffix Async in the name is part of the adopted standard.

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

For more information see documentation.

Documentation of SendAsync.

Already has a question on the subject.

More examples general use of async.

Response in the OS with a more complete example.

2

This would be asynchronous (not at the same time). Use the ELMAH.MVC bug 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.")));
    }
}
  • As it seems that the code came from somewhere else you could quote the source.

  • Absolutely. Based here:http://stackoverflow.com/a/8758053/3973284.

0

A good solution would be to use a webservice to send the emails, you only need to put the emails in a persistent queue and the sending service would do the job without rushing and without charging the application server, you can use a service ready as the Mailgun or the SES Amazon or create own shipping service that is not so complicated.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.