Asynchronous methods of Smtp class


Introduction

Async/await methods of Smtp class allow your application to send e-mails in the background using lightweight I/O worker threads.

Actually, MailBee.NET library also has two sets of async methods: - MethodNameAsync - truly async methods, supported in .NET 4.5 and higher. They are fast, scalable and easy to use with async/await pattern. This tutorial focuses on async/await approach. - BeginMethodName/EndMethodName pairs - they create CPU-bound worker threads. They are not as efficient and now considered deprecated. Use them only if you're bound to an older version of .NET Framework (prior to v4.5). You can find examples of these methods in MailBee.NET library class reference (such as Smtp.BeginSend) and in .NET 2.0+ versions of sample projects (see below).

Note that there is a difference between multi-threaded mode (see Smtp.MaxThreadCount property) and async methods. For instance, multi-threading can be used by a regular synchronous method as long as an async method can send e-mails in a single worker thread (without multi-threading).

An async method just allows your application to proceed immediately with other tasks not waiting for completion of the operation started. However, you cannot start another async operation on the same instance of Smtp class until the previous operation has finished. If you planned to use async operations to send multiple e-mails simultaneously, use jobs instead (see Send bulk e-mail through two SMTP relay servers for increased throughput topic for details).

Besides the samples below, there is a complete WinForms sample project SMTP Demo 2 which sends an e-mail asynchronously and carefully handles exceptions. It's installed with the product among other sample projects in "My Documents\MailBee.NET Objects\Samples\WinForms" folder. The project is available in both C# and VB versions, for .NET 2.0+ (BeginSend-based) and .NET 4.5+ (SendAsync-based).

Another useful sample project there is AddressValidator Demo. Although it's for another MailBee.NET's component EmailAddressValidator, the idea of using async methods and handling exceptions is all the same for all MailBee.NET components.


Send single e-mail asynchronously

This is a simple console sample of sending an e-mail in async way:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MailBee.SmtpMail;

class Program
{
	private static async Task DoAsyncWork()
	{
		Smtp mailer = new Smtp();

		// Use SMTP relay with authentication.
		mailer.SmtpServers.Add("mail.here.com", "joe", "secret");

		// Set other fields of the e-mail.
		mailer.Message.From.AsString = "Joe D. <joe@here.com>";
		mailer.Message.To.AsString = "jane@example.com";
		mailer.Message.Subject = "Message subject";
		mailer.Message.BodyHtmlText = "<html>Message text</html>";

		// Send asynchronously.
		await mailer.SendAsync();

		Console.WriteLine("The e-mail was sent to: " +
			mailer.GetAcceptedRecipients().ToString());
	}

	static void Main(string[] args)
	{
		DoAsyncWork().Wait();
		Console.WriteLine("Press any key to exit");
		Console.ReadKey();
	}
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports MailBee.SmtpMail

Module Module1
	Private Async Function DoAsyncWork() As Task
		Dim mailer As New Smtp()

		' Use SMTP relay with authentication.
		mailer.SmtpServers.Add("mail.here.com", "joe", "secret")

		' Set other fields of the e-mail.
		mailer.Message.From.AsString = "Joe D. <joe@here.com>"
		mailer.Message.To.AsString = "jane@example.com"
		mailer.Message.Subject = "Message subject"
		mailer.Message.BodyHtmlText = "<html>Message text</html>"

		' Send asynchronously.
		Await mailer.SendAsync()

		Console.WriteLine("The e-mail was sent to: " & mailer.GetAcceptedRecipients().ToString())
	End Function

	Sub Main(args As String())
		DoAsyncWork().Wait()
		Console.WriteLine("Press any key to exit")
		Console.ReadKey()
	End Sub
End Module

We assume MailBee.NET SMTP license key is already set in app.config or Windows registry. See Import namespaces and set license key topic for details.

You can find more complex WinForms sample suitable for use in a desktop application at Smtp.BeginSend method documentation.

Also, as mentioned above, the complete WinForms sample project SMTP Demo 2 which uses Smtp.BeginSend method is installed with the product.


Send multiple e-mails asynchronously

To send more than one e-mail or perform mail merge, use Smtp.BeginSendJobs method. The sample below is an async version of a similar code from Send series of e-mails based on template and database topic:

using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Program
{
	// Create DataTable with some data. In real-world apps,
	// you'll get the data from external sources like SQL query.
	private static DataTable GetData()
	{
		DataTable workTable = new DataTable("customers");

		DataColumn workCol = workTable.Columns.Add("id", typeof(int));
		workCol.AllowDBNull = false;
		workCol.Unique = true;

		workTable.Columns.Add("name", typeof(string));
		workTable.Columns.Add("email", typeof(string));
		workTable.Columns.Add("doc_id", typeof(int));
		workTable.Columns.Add("doc_name", typeof(string));

		// Add two rows to the data source. This way,
		// the mail merge procedure will generate two e-mails.
		DataRow row = workTable.NewRow();
		row["id"] = 1;
		row["name"] = "John Doe";
		row["email"] = "john.doe@company.com";
		row["doc_id"] = 637;
		row["doc_name"] = "annual report.xls";
		workTable.Rows.Add(row);

		row = workTable.NewRow();
		row["id"] = 2;
		row["name"] = "Bob Brown";
		row["email"] = "bob.brown@example.com";
		row["doc_id"] = 2358;
		row["doc_name"] = "agreement draft.pdf";
		workTable.Rows.Add(row);

		return workTable;
	}

	private static async Task DoAsyncWork()
	{
		Smtp mailer = new Smtp();

		// Use SMTP relay server with authentication.
		mailer.SmtpServers.Add("mail.here.com", "jane.doe", "secret");

		// Set static From and Subject.
		mailer.Message.From.AsString = "Jane Doe <jane.doe@here.com>";
		mailer.Message.Subject = "Information";

		// Set templates for To, body, and attachment.
		mailer.Message.To.AsString = "##name## <##email##>";
		mailer.Message.BodyPlainText = "Attachment for ##name##";
		mailer.Message.Merge.AddAttachmentPattern(@"C:\Docs\##doc_name##");

		// Enqueue mail merge job.
		mailer.AddJob("async mail merge sample", null, null, GetData());

		await mailer.SendJobsAsync();

		Console.WriteLine("The newsletter was sent to: " +
			mailer.JobsSuccessful.Count.ToString() + " addresses");

		Console.WriteLine("The newsletter wasn't sent to: " +
			mailer.JobsFailed.Count.ToString() + " addresses");
	}

	static void Main(string[] args)
	{
		DoAsyncWork().Wait();
		Console.WriteLine("Press any key to exit");
		Console.ReadKey();
	}
}
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports MailBee.SmtpMail

Module Module1
	' Create DataTable with some data. In real-world apps,
	' you'll get the data from external sources like SQL query.
	Private Function GetData() As DataTable
		Dim workTable As New DataTable("customers")

		Dim workCol As DataColumn = _
			workTable.Columns.Add("id", GetType(Integer))
		workCol.AllowDBNull = False
		workCol.Unique = True

		workTable.Columns.Add("name", GetType(String))
		workTable.Columns.Add("email", GetType(String))
		workTable.Columns.Add("doc_id", GetType(Integer))
		workTable.Columns.Add("doc_name", GetType(String))

		' Add two rows to the data source. This way,
		' the mail merge procedure will generate two e-mails.
		Dim row As DataRow = workTable.NewRow()
		row("id") = 1
		row("name") = "John Doe"
		row("email") = "john.doe@company.com"
		row("doc_id") = 637
		row("doc_name") = "annual report.xls"
		workTable.Rows.Add(row)

		row = workTable.NewRow()
		row("id") = 2
		row("name") = "Bob Brown"
		row("email") = "bob.brown@example.com"
		row("doc_id") = 2358
		row("doc_name") = "agreement draft.pdf"
		workTable.Rows.Add(row)

		Return workTable
	End Function

	Private Async Function DoAsyncWork() As Task
		Dim mailer As New Smtp()

		' Use SMTP relay server with authentication.
		mailer.SmtpServers.Add("mail.here.com", "jane.doe", "secret")

		' Set static From and Subject.
		mailer.Message.From.AsString = "Jane Doe <jane.doe@here.com>"
		mailer.Message.Subject = "Information"

		' Set templates for To, body, and attachment.
		mailer.Message.To.AsString = "##name## <##email##>"
		mailer.Message.BodyPlainText = "Attachment for ##name##"
		mailer.Message.Merge.AddAttachmentPattern("C:\Docs\##doc_name##")

		' Enqueue mail merge job.
		mailer.AddJob("async mail merge sample", Nothing, Nothing, GetData())

		' Start mail merge asynchronously.
		Await mailer.SendJobsAsync()

		Console.WriteLine("The newsletter was sent to: " & _
			mailer.JobsSuccessful.Count.ToString() & " addresses")

		Console.WriteLine("The newsletter wasn't sent to: " & _
			mailer.JobsFailed.Count.ToString() & " addresses")
	End Function

	Sub Main(args As String())
		DoAsyncWork().Wait()
		Console.WriteLine("Press any key to exit")
		Console.ReadKey()
	End Sub
End Module

With async methods, you can also use events and all other features which are available for synchronous methods. However, be sure that the main thread of the application (message loop thread) is not blocked. Otherwise, events won't get a chance to get processed and the application will hang.

Async methods available for all synchronous methods (i.e. Smtp.ConnectAsync for Smtp.ConnectSmtp.ExecuteCustomCommandAsync for Smtp.ExecuteCustomCommand, and so on). Their usage is very similar to synchronous methods.


Send feedback to AfterLogic

Copyright © 2006-2023 AfterLogic Corporation. All rights reserved.