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
How to Tryparse for Enum Value
Html.Enumdropdownlistfor: Showing a Default Text
How to Get the Current Date Without the Time
From Excel to Datatable in C# with Open Xml
How to Add Event Handler for Dynamically Created Controls at Runtime
Login to the Page with Httpwebrequest
Customattribute Reflects HTML Attribute MVC5
How to Check If a Property Exists on a Dynamic Anonymous Type in C#
Dynamic Lang. Runtime VS Reflection
Cannot Load Counter Name Data Because an Invalid Index -Exception
C# Reflection - Load Assembly and Invoke a Method If It Exists
Owin's Getexternallogininfoasync Always Returns Null
Improve Wpf Datagrid Performance
How to Display a Windows Form in Full Screen on Top of the Taskbar