Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two ways to send email via SmtpClient asynchronously, different results

Simple concept here. This is for a site being built using MVC 3 and Entity Framework 4. After a user registers on the site, an email is sent to their email address. I first implemented this using SmtpClient.Send() and it worked fine. Then I got the bright idea to try sending the email asynchronously. I'm experiencing issues with the two async approaches I've tried.

First implementation (from this unanswered post: https://stackoverflow.com/questions/7558582/how-to-dispose-using-smtpclient-send-and-asynccallback):

public bool Emailer(){
    .
    .
    .
    using (var smtpClient = new SmtpClient())
    {
        smtpClient.EnableSsl = true;
        smtpClient.Host = "smtp.gmail.com";
        smtpClient.Port = 587;
        smtpClient.UseDefaultCredentials = false;
        smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword");

        var sd = new SendEmailDelegate(smtpClient.Send);
        var cb = new AsyncCallback(SendEmailResponse);
        sd.BeginInvoke(message, cb, sd);

        return true;
    }
}

private delegate void SendEmailDelegate(System.Net.Mail.MailMessage m);

private static void SendEmailResponse(IAsyncResult ar)
{
    try
    {
        SendEmailDelegate sd = (SendEmailDelegate)(ar.AsyncState);
        sd.EndInvoke(ar); // "cannot access a disposed object" errors here
    }
    catch (Exception e)
    {
        _logger.WarnException("Error on EndInvoke.", e);
    }
}

This worked half the time. The other half I would get a "Cannot access a disposed object" error in the CallBack.

Next implementation (from a member with solid reputation: What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0):

var smtpClient = new SmtpClient();
smtpClient.EnableSsl = true;
smtpClient.Host = "smtp.gmail.com";
smtpClient.Port = 587;
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword");

smtpClient.SendCompleted += (s, e) =>
    {
        smtpClient.Dispose();
        message.Dispose();
    };
smtpClient.SendAsync(message, null);

With this implementation I dont get any errors, but there's a noticeably longer delay (~5 seconds) in debugging mode when smtpClient.SendAsync() executes, leading me to think that its not being sent asynchronously.

Questions:

1) what's wrong in the first method that's causing the "disposed object" errors?

2) does the second implementation have a problem that's causing the email to not send asynchronously? Is the 5-second delay meaningless?

Might be also important to note that although the site will not need to support sending a large number of emails (only user registration, opt-in newsletters, etc), we do anticipate a large number of users in the future, hence my decision to send emails asynchronously.

Thanks.

like image 306
John L Avatar asked Jan 07 '12 10:01

John L


People also ask

Is SMTP synchronous or asynchronous?

Well, the "send" to the smtp server is synchronous, but if you were trying to make sure the message got to its intended destination, you're out of luck. SmtpClient will wash its hands of the operation as soon as the Smtp server acknowledges receipt. Save this answer. Show activity on this post.

Is SmtpClient obsolete?

The SmtpClient type is obsolete on some platforms and not recommended on others; for more information, see the Remarks section.

What is SmtpClient in c#?

SMTP Client in C# and VB.NETThe Simple Mail Transfer Protocol (SMTP) is the only standard protocol for sending mail messages over the Internet. GemBox. Email enables you to work with the SMTP protocol in C# and VB.NET using an SmtpClient class.


2 Answers

Your fist method will not work properly because of the USING-block. After the using-block ends, the SmtpClient object will de disposed. So you can't get access to it in your event handler.

like image 104
Mithrandir Avatar answered Oct 26 '22 03:10

Mithrandir


tips: 1-dont use "using block" for MailMessage objet, it dispose your object before mail sent
2-dispose MailMessage objects on SmtpClient.SendCompleted event:

smtpClient.SendCompleted += (s, e) =>
    {
        message.Dispose();
    };

3-set SendCompletedEventHandler for smtpClient object

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

4-more code:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        // Get the unique identifier for this asynchronous operation.
        String token = (string)e.UserState;

        if (e.Cancelled)
        {
            //write your code here
        }
        if (e.Error != null)
        {
            //write your code here
        }
        else //mail sent
        {
            //write your code here
        }

        mailSent = true;
    }
like image 22
daszarrin Avatar answered Oct 26 '22 03:10

daszarrin