tofutim
tofutim

Reputation: 23374

What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0

I'm a bit perplexed on how to manage SmtpClient now that it is disposable, especially if I make calls using SendAsync. Presumably I should not call Dispose until SendAsync completes. But should I ever call it (e.g., using "using"). The scenario is a WCF service which mails out email periodically when calls are made. Most of the computation is fast, but the sending of email can take a second or so, so Async would be preferable.

Should I create a new SmtpClient each time I send mail? Should I create one for the entire WCF? Help!

Update In case it makes a difference, each email is always customized to the user. The WCF is hosted on Azure and Gmail is used as the mailer.

Upvotes: 129

Views: 102024

Answers (6)

Hasan Batuhan Kurt
Hasan Batuhan Kurt

Reputation: 281

I used this way in asp.net 5.0 core.

public async Task EmailSend(MessageModel messageModel)
    {
        using (MailMessage mailMessage = new MailMessage())
        {
            mailMessage.From = new MailAddress(_configuration.GetSection("EmailConfiguration").GetSection("FromEmail").Value.ToString(), _configuration.GetSection("EmailConfiguration").GetSection("FromName").Value.ToString(), Encoding.UTF8);
            mailMessage.Subject = messageModel.Subject;
            mailMessage.SubjectEncoding = Encoding.UTF8;
            mailMessage.Body = messageModel.Content;
            mailMessage.BodyEncoding = Encoding.UTF8;
            mailMessage.IsBodyHtml = true;
            mailMessage.BodyTransferEncoding = TransferEncoding.Base64;
            mailMessage.To.Add(new MailAddress(messageModel.To));
            NetworkCredential networkCredential = new NetworkCredential(_configuration.GetSection("EmailConfiguration").GetSection("Username").Value.ToString(), _configuration.GetSection("EmailConfiguration").GetSection("Password").Value.ToString());
            SmtpClient smtpClient = new SmtpClient();
            smtpClient.Host = _configuration.GetSection("EmailConfiguration").GetSection("SmtpServer").Value.ToString();
            smtpClient.EnableSsl = Convert.ToBoolean(_configuration.GetSection("EmailConfiguration").GetSection("SSL").Value);
            smtpClient.UseDefaultCredentials = Convert.ToBoolean(_configuration.GetSection("EmailConfiguration").GetSection("UseDefaultCredentials").Value);
            smtpClient.Port = Convert.ToInt32(_configuration.GetSection("EmailConfiguration").GetSection("Port").Value);
            smtpClient.Credentials = networkCredential;
            smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
            await smtpClient.SendMailAsync(mailMessage);
        }
    }

Upvotes: 1

TheCodeKing
TheCodeKing

Reputation: 19220

Note: .NET 4.5 SmtpClient implements async awaitable method SendMailAsync. For lower versions, use SendAsync as described below.


You should always dispose of IDisposable instances at the earliest possibility. In the case of async calls, this is on the callback after the message is sent.

var message = new MailMessage("from", "to", "subject", "body"))
var client = new SmtpClient("host");
client.SendCompleted += (s, e) => {
                           client.Dispose();
                           message.Dispose();
                        };
client.SendAsync(message, null);

It's a bit annoying the SendAsync doesn't accept a callback.

Upvotes: 151

Boris Lipschitz
Boris Lipschitz

Reputation: 9516

The original question was asked for .NET 4, but if it helps as of .NET 4.5 SmtpClient implements async awaitable method SendMailAsync.

As a result, to send email asynchronously is as the following:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    using (var message = new MailMessage())
    {
        message.To.Add(toEmailAddress);

        message.Subject = emailSubject;
        message.Body = emailMessage;

        using (var smtpClient = new SmtpClient())
        {
            await smtpClient.SendMailAsync(message);
        }
    }
}

It's better to avoid using SendAsync method.

Upvotes: 191

Anton Berlinsky
Anton Berlinsky

Reputation: 615

You can see why it is particularly important to dispose of SmtpClient by the following comment:

public class SmtpClient : IDisposable
   // Summary:
    //     Sends a QUIT message to the SMTP server, gracefully ends the TCP connection,
    //     and releases all resources used by the current instance of the System.Net.Mail.SmtpClient
    //     class.
    public void Dispose();

In my scenario sending multiple mails using Gmail without disposing the client, I used to get:

Message: Service not available, closing transmission channel. The server response was: 4.7.0 Temporary System Problem. Try again later (WS). oo3sm17830090pdb.64 - gsmtp

Upvotes: 6

jmelhus
jmelhus

Reputation: 1140

Ok, old question I know. But I stumbled upon this myself when I was in need of implementing something similar. I just wanted to share some code.

I'm iterating over several SmtpClients to send several mail asynchronously. My solution is similar to TheCodeKing, but I'm disposing the callback object instead. I'm also passing MailMessage as userToken to get it in the SendCompleted event so I can call dispose on that as well. Like this:

foreach (Customer customer in Customers)
{
    SmtpClient smtpClient = new SmtpClient(); //SmtpClient configuration out of this scope
    MailMessage message = new MailMessage(); //MailMessage configuration out of this scope

    smtpClient.SendCompleted += (s, e) =>
    {
        SmtpClient callbackClient = s as SmtpClient;
        MailMessage callbackMailMessage = e.UserState as MailMessage;
        callbackClient.Dispose();
        callbackMailMessage.Dispose();
    };

    smtpClient.SendAsync(message, message);
}

Upvotes: 6

jeroenh
jeroenh

Reputation: 26782

In general, IDisposable objects should be disposed as soon as possible; implementing IDisposable on an object is intended to communicate the fact that the class in question holds expensive resources that should be deterministically released. However, if creating those resources is expensive and you need to construct a lot of these objects, it may be better (performance wise) to keep one instance in memory and reuse it. There's only one way to know if that makes any difference: profile it!

Re: disposing and Async: you can't use using obviously. Instead you typically dispose the object in the SendCompleted event:

var smtpClient = new SmtpClient();
smtpClient.SendCompleted += (s, e) => smtpClient.Dispose();
smtpClient.SendAsync(...);

Upvotes: 16

Related Questions