Asynchronously Sending Emails in C#

Asynchronously sending Emails in C#?

As of .NET 4.5 SmtpClient implements async awaitable method
SendMailAsync.
As a result, to send email asynchronously is as following:

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);
}
}

Send email in asynchronous method

You can change the implementation of SendEmail to:

private async Task<int> SendEmail(System.Net.Mail.SmtpClient smtp, 
System.Net.Mail.MailMessage m)
{
await smtp.SendAsync(m).ConfigureAwait(false);
return 0;
}

Also see the following url:
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Async all the way
Don’t mix blocking and async code.

Configure context
Use ConfigureAwait(false) when you can.

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#?

Sending Email asynchronously in ASP.NET C#

The reason is mentioned here on the MSDN documentation on SmtpClient.SendAsync

After calling SendAsync, you must wait for the e-mail transmission to
complete before attempting to send another e-mail message using Send
or SendAsync.

You could call SendAsync using ThreadPool thru a wrapped method call. This would give you something like a fire-and-forget scenario.

Something like this:

public void SendViaThread(System.Net.Mail.MailMessage message) {
try {
System.Threading.ThreadPool.QueueUserWorkItem(SendViaAsync, message);
} catch (Exception ex) {
throw;
}
}

private void SendViaAsync(object stateObject) {
System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();
System.Net.Mail.MmailMessage message = (MmailMessage)stateObject;
...
smtp.Credentials = new NetworkCredential("...");
smtp.Port = 587;
smtp.Host = "...";
...
smtp.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
...
smtp.Send(message);
}

And you would call SendViaThread(mail) in your code. So, your loop now becomes:

for (int i = 0; i < lista.Count; i++) {
MailMessage mail = new MailMessage();
mail.From = new MailAddress("...");
mail.To.Add(lista[i].Email);
mail.Subject = "...";
...
mail.Body = "...";
SendViaThread(mail);
}

Send Email async method

Create an async wrapper

public Task void SendEmailAsyn() { 
return Task.Factory.StartNew(() => { SendEmail(); });
}

How to send Newsletters (bulk emails) asynchronously in ASP.NET

Take a look at the async and await keywords.

https://msdn.microsoft.com/en-us/library/mt674882.aspx

The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in the .NET Framework or the Windows Runtime to create an asynchronous method almost as easily as you create a synchronous method. Asynchronous methods that you define by using async and await are referred to as async methods.

MSDN explains the syntax side of things. The bigger concern is error handling and reliably. Dumping 5,000 emails into a list and hitting the "send" button on them is a little optimistic. Do these emails need to be reliably delivered? What happens if 3,000 of them send, and a network error suddenly causes temporary connectivity loss to the outgoing mail server? Are you going to resend all 5,000 when it starts working again? Just forget about the last 2,000? Are the recipients going to be mad because they got duplicates, or didn't get the message at all? How are you going to troubleshoot errors?

A pattern that I've found that has worked really well (whether you are sending synchronously or asynchronously), is to generate the messages and store each in a database table, and then use something like the following:

public void SendAllEmails()
{
var emails = SomeClass.GetAllUnsentEmails();

foreach(Email message in Emails)
{
var success = SendEmail(message);

if (!success)
{
// Do you want to do something if it fails?
}
}
}

public bool SendEmail(Email message)
{
try
{
// 1. Send the email message
// 2. Update the "SentOn" date in the database
// 3. return true
}
catch(Exception ex)
{
SomeClass.CreateEmailErrorEntry(message, ex); // store error in a table or log
return false;
}
}


Related Topics



Leave a reply



Submit