What Are Best Practices for Using Smtpclient, Sendasync and Dispose Under .Net 4.0

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

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.

SmtpClient.SendAsync() not sending email if wrapped in using()

You need to use a callback to get the result of the SendAsync call.

Also because you are inside a "using" statement the SmtpClient instance gets disposed before it gets a chance to finish the request.

You get the result of your call like this:

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

if (e.Cancelled)
{
Console.WriteLine("[{0}] Send canceled.", token);
}
if (e.Error != null)
{
Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
} else
{
Console.WriteLine("Message sent.");
}
mailSent = true;
}

//.... in some other place in the code where your SmptClient lives
client.SendCompleted += new
SendCompletedEventHandler(SendCompletedCallback);
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, the userToken is a string constant.

client.SendAsync(message, userState);
//wait for the mail to be sent
while(!mailSent)
{
}

And basically with SmtpClient the SendAsync works if you do not dispose the instance until after the request is over.

Basically you could potentially dispose of the instance in the completed event or by waiting on a boolean for example.

It is not ideal but then again the SmtpClient class is obsolete.

The example was taken directly from Microsoft's documentation SmtpClient

Send mail with smtp SendAsync

Change your method to:

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

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

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

And call it like:

var task = SendEmail(toEmailAddress, emailSubject, emailMessage);
var result = task.WaitAndUnwrapException();

Have a look here Asynchronously sending Emails in C#?
and here How to call asynchronous method from synchronous method in C#?

Dispose SmtpClient in SendComplete?

You should dispose both the MailMessage and the SmtpClient in SendAsyncCallback.

Disposing the MailMessage will not dispose the SmtpClient automatically (because you might want to send two messages with the same SmtpClient, and you wouldn't want the client to be disposed as soon as you disposed the first message).

Two ways to send email via SmtpClient asynchronously, different results

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.

SendAsync Smtp Mail Error

You are disposing the message before the send can complete. You should be disposing them in the SendCompleted callback event. Here's an example of the best way to dispose the client and the message.

Your code should look something like this:

var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};

var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body,
};

smtp.SendCompleted += (s, e) => {
SendCompletedCallback(s, e);
smtp.Dispose();
message.Dispose();
};
string userState = "Test";
message.Attachments.Add(new Attachment(Dir));
smtp.SendAsync(message, userState);

SmtpClient Class: send() works but not sendAsync()

The problem is in here:

message.Dispose()

You're freeing your resources before email is sent. You should free your resources only in the SendCompletedCallback, because right now you simply say: start sending this… oh, no, never mind.

SmtpClient - What is proper lifetime?

You should always utilise using

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

In most cases you should always dispose of anything that implements IDisposable as soon as you are finished with it, however you should always check the Docs to be sure. The SmtpClient class in .NET 4.0 implements IDisposable so be sure to use it!

To quote MSDN:

The SmtpClient class has no Finalize method, so an application must
call Dispose to explicitly free up resources.

If you find yourself doing async related tasks then you can make a new instance for each email to prevent blocking yourself.You can use the following.

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

At Request - Best option for Bulk Send Emails

As noted above you can reuse the same client. If you keep it all on the same thread I recommend you just use one client

MSDN States:

The SmtpClient class implementation pools SMTP connections so that it
can avoid the overhead of re-establishing a connection for every
message to the same server. An application may re-use the same
SmtpClient object to send many different emails to the same SMTP
server and to many different SMTP servers.

However it goes on to say:

...As a result, there is no way to determine when an application is
finished using the SmtpClient object and it should be cleaned up.

So assuming you dispose of your Client when complete it is fine.


There is discussion of a number of SMTP related topics linked below as I recently found myself asking the same question

More from Stackoverflow:

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

How to dispose objects having asynchronous methods called?

Related Reading:

MSDN SmtpClient

Implementing Finalize and Dispose to clean up managed resources

Sending e-mail from computer on network - .NET 4.0

You have to install a SMTP server on your localhost to be able to send mail.

Outlook only receives e-mails through POP3 or IMAP, etc.

edit:
i.e. you need

  1. some server that accepts mail through SMTP from your client and forwards it to its destination; and
  2. some server that accepts mail (normally through SMTP) and stores it in a mailbox, so people can retrieve them later through POP3 or IMAP or whatever means.

Your company mail server should normally do both.


edit2:

You might be able to cheat and use SMTPClient to deliver the mail to the receiver's mailbox server directly though.

Try resolving for the MX record (see How to get mx records for a dns name with System.Net.DNS?) and create a SMTPClient directly to the best MX server returned.

If Microsoft implemented enough of the SMTP specification and your host is not treated as sending spam, the mail should go through.



Related Topics



Leave a reply



Submit