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
- some server that accepts mail through SMTP from your client and forwards it to its destination; and
- 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
Htmlagilitypack -- Does <Form> Close Itself for Some Reason
How to Read Regex Captures in C#
MVC Web API: No 'Access-Control-Allow-Origin' Header Is Present on the Requested Resource
Passing an Enum Value as Command Parameter from Xaml
Displayname Attribute VS Display Attribute
How to Pass a Username/Password in the Header to a Soap Wcf Service
Why Is It Considered Bad to Expose List<T>
The Name "Xyz" Does Not Exist in the Namespace "Clr-Namespace:Abc"
How to Round Up Value C# to the Nearest Integer
How to Turn Off or Handle Camelcasing in JSON Response ASP.NET Core
Put Wpf Control into a Windows Forms Form
C# Windows Forms Application - Updating Gui from Another Thread and Class
Why Are Subjects Not Recommended in .Net Reactive Extensions
Getting All Controllers and Actions Names in C#
Checking If a String Array Contains a Value, and If So, Getting Its Position
Determine If Current Application Is Activated (Has Focus)
Why Does Stylecop Recommend Prefixing Method or Property Calls with "This"