0
namespace Binarios.admin
{
    public class SendEmailGeral
    {
        public SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
        public MailMessage msg = new MailMessage();

        public void Enviar(string sendFrom, string sendTo, string subject, string body)
        {    
            string pass = "12345";
            System.Net.NetworkCredential smtpCreds = new System.Net.NetworkCredential(sendFrom, pass);

            //setup SMTP Host Here
            client.UseDefaultCredentials = false;
            client.Credentials = smtpCreds;
            client.EnableSsl = true;

            MailAddress to = new MailAddress(sendTo);
            MailAddress from = new MailAddress(sendFrom);

            msg.IsBodyHtml = true;
            msg.Subject = subject;
            msg.Body = body;
            msg.From = from;
            msg.To.Add(to);

            client.Send(msg);
        }
    }
}

I've this code, but i'd like to improve it in way that i could send mails asynchronous. Could you suggest any idea to improve this piece of code or other way to do it. I've tried asynchronous properties that visual studio suggested but couldn't use them.

Severiano
  • 1,083
  • 3
  • 25
  • 54
  • 2
    Have you actually looked at the documentation? http://msdn.microsoft.com/en-us/library/x5x13z6h.aspx – Arran Feb 28 '13 at 16:17
  • Try searching before??? [Asynchronously sending Emails in C#?](http://stackoverflow.com/questions/3408397/asynchronously-sending-emails-in-c) .. [Two ways to send email via SmtpClient asynchronously](http://stackoverflow.com/questions/8768863/two-ways-to-send-email-via-smtpclient-asynchronously-different-results) – huMpty duMpty Feb 28 '13 at 16:20

3 Answers3

11

SmtpClient allows you to send asynchronously, and uses events to notify you when the send completes. This can be unweildy to use, so you can create an extension method to return a Task instead:

public static Task SendAsync(this SmtpClient client, MailMessage message)
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    Guid sendGuid = Guid.NewGuid();

    SendCompletedEventHandler handler = null;
    handler = (o, ea) =>
    {
        if (ea.UserState is Guid && ((Guid)ea.UserState) == sendGuid)
        {
            client.SendCompleted -= handler;
            if (ea.Cancelled)
            {
                tcs.SetCanceled();
            }
            else if (ea.Error != null)
            {
                tcs.SetException(ea.Error);
            }
            else
            {
                tcs.SetResult(null);
            }
        }
    };

    client.SendCompleted += handler;
    client.SendAsync(message, sendGuid);
    return tcs.Task;
}

To get the result of the send task you can use ContinueWith:

Task sendTask = client.SendAsync(message);
sendTask.ContinueWith(task => {
    if(task.IsFaulted) {
        Exception ex = task.InnerExceptions.First();
        //handle error
    }
    else if(task.IsCanceled) {
        //handle cancellation
    }
    else {
        //task completed successfully
    }
});
Lee
  • 142,018
  • 20
  • 234
  • 287
  • this is giving me an error now, it says: Failure sending mail. – Severiano Feb 28 '13 at 17:03
  • @DiogoSeveriano - Firstly, there was a bug in the code I initially posted which would cause a stack overflow exception. If you're using the current version of the code, you should be able to get the inner exception from the task when it completes. What's the exception? – Lee Feb 28 '13 at 17:06
  • i solved the problem with Async="true" in the page, that was the problem, but now i think this doesn't send asynchronous because i still have to wait to this to send the mails and then i can do something. – Severiano Feb 28 '13 at 17:12
  • @DiogoSeveriano - You shouldn't have to wait for the `Task` to complete. Are you calling `Task.Wait()`? That will cause the current thread to block until the task completes. You should use `ContinueWith` to get the result of the task when it has completed. Alternatively you can use `await sendTask` if you're using C#5. – Lee Feb 28 '13 at 17:16
  • i'm not using anything else, unless what you gave to me. – Severiano Feb 28 '13 at 17:19
  • SendAsync(client, msg); how can i use ContinueWith, i'm an amateur with threading – Severiano Feb 28 '13 at 17:23
  • @DiogoSeveriano - I've updated the answer to include an example of using `ContinueWith`. – Lee Feb 28 '13 at 17:29
  • Your code will not work when somebody tries to send several e-mails simultaneously using the single SmtpClient. – Der_Meister Dec 21 '16 at 11:29
1

Wild guess, but SendAsync might do the job!

Sean
  • 60,939
  • 11
  • 97
  • 136
0

Change your code from:

 client.Send(msg);

To:

client.SendAsync(msg);

more details

link1

link2

Niventh
  • 985
  • 2
  • 9
  • 23