Click here to Skip to main content
15,867,835 members
Articles / Web Development / HTML

MailMergeLib - A Mail Client Library for .NET

Rate me:
Please Sign up or sign in to vote.
4.95/5 (131 votes)
5 Nov 2017MIT9 min read 832.1K   10.2K   542   273
MailMergeLib is an SMTP template mail client library written in C# which provides comfortable mail merge capabilities and SMTP fail-over features. If works on .NET Framework and .NET Core.

Image 1

Screenshot of a merged email

The Evolution

Back in 2007, after MailMergeLib was completed, it turned out that Microsoft System.Net.Mail had about 20 ugly issues. Many of them could be fixed by hacking their system with System.Reflection. The drawback was that every new release of the .NET Framework required extensive testing and rewriting the bug fixes.

The bugs were reported to Microsoft, and eventually half of them were corrected.

In the beginning of 2015, Jeffrey Stedfast introduced the first releases of his MimeKit and MailKit open source libraries. As he wrote in his Code Review, he was convinced that there could be something better than System.Net.Mail. And he really succeeded - not only in generating RFC compliant outgoing mail messages, but also in providing IMAP and POP3 facilities. This was the starting point for rewriting the code of MailMergeLib, now using MailKit and MimeKit instead of System.Net.Mail.

Introduction

There are numerous occasions where an application has to send e-mails: Feedback forms in the web, webmailers, loggers and so forth. And there are many commercial solutions available: EASendMail SMTP Component, MailBee.NET SMTP Component, Aspose.Network.Mail or aspNetEmail, just to mention some of them. If you're looking for a free solution with source code, supplying comparable features, you'll find it worthwhile to have a closer look at MailMergeLib:

Mail Message Generation

  • Email templates can be fully individualized in terms of recipients, subject, HTML and/or plain text, attachments and even headers. Placeholders are inserted as variable names from data source between curly braces like so: {MailboxAddress.Name} or with formatting arguments like {Date:yyyy-MM-dd}. (In the first releases of MailMergeLib, this was accomplished with regular expressions. Now the extremely fast SmartFormat parser is implemented.)
  • HTML text may contain images from local hard disk, which will be automatically inserted as inline attachments.
  • For HTML text MailMergeLib can generate a plain text representation.
  • Attachment sources can be files, streams or strings.
  • The data source for email merge messages to a number of recipients and can be any IEnumerable object as well as DataTables. The data source for single emails can be any of the following types: Dictionary<string,object>, ExpandoObject, DataRow, any class instances or anonymous types. For class instances, it's even allowed to use the name of parameter less methods.
  • Placeholders in the email can be formatted with any of the features known from string.Format by using SmartFormat.NET. SmartFormat is a parser coming close to string.Format's speed, but bringing a lot of additional options like easy pluralization for many languages.
  • Resulting emails are MimeMessages from MimeKit, an outstanding tool for creating and parsing emails, covering all relevant MIME standards making sure that emails are not qualified as SPAM.
  • Support for international email address format.

Sending Email Messages

  • Practically unlimited number of parallel tasks to send out individualized emails to a big number of recipients.
  • SmptClients for each task can get their own preconfigured settings, so that e.g. several mail servers can be used for one send job.
  • Progress of processing emails can easily be observed with a number of events.
  • SMTP failures can automatically be resolved supplying a backup configuration. This fault-tolerance is essential for unattended production systems.
  • SMTP configuration can be stored/restored to/from standard XML files.
  • Emails are sent using the SmptClients from MailKit, the sister project to MimeKit. MailKit is highly flexible and can be configured for literally every scenario you can think of.
  • Instead of sending, emails can also be stored in MIME formatted text files, e.g. if a "pickup directory" from IIS or Microsoft Exchange shall be used. If needed, these files can be loaded back into a MimeMessage from MimeKit.

General

  • Fine grained control over the whole process of email message generation and distribution.
  • Clearly out-performs .NET System.Net.Mail.
  • Configuration settings for messages and SMTP can be stored to and loaded from an XML file.
  • RFC standards compliant.
  • We ask you not to use MailMergeLib for sending unsolicited bulk email.

Using the Code

Configuration

Basic Settings

The configuration of MailMergeLib consists of two major parts: MessageConfig and SenderConfig.

First of all, set the key which is used to encrypt and decrypt network credentials when the configuration is saved to file. This is not absolutely safe, but better than showing credentials as plain text:

JavaScript
Settings.CryptoKey = "SecretCryptoKey";  // don't use this key

A rather minimal configuration looks like this:

JavaScript
var settings = new Settings
{
    MessageConfig =
    {
        CharacterEncoding = Encoding.UTF8,
        StandardFromAddress = new MailboxAddress("sender name", "sender@example.com"),
        // necessary for proper formatting of placeholders:
        CultureInfo = CultureInfo.CurrentCulture,
        // especially in case a placeholder results in an empty address:
        IgnoreIllegalRecipientAddresses = true,
        Xmailer = "MailMergeLib 5"
    },
    SenderConfig =
    {
        SmtpClientConfig = new[]
        {
            new SmtpClientConfig()
            {
                MessageOutput = MessageOutput.SmtpServer,
                SmtpHost = "some.host.com",
                SmtpPort = 587,
                NetworkCredential = new Credential("user", "password"),
                // identify active configuration in case you have more than one
                Name = "StandardConfig",
                // number of trials until an exception is thrown:
                MaxFailures = 3,  
                // number of milliseconds before a retry to send will happen:
                RetryDelayTime = 1000,   
                // number of milliseconds between messages:
                DelayBetweenMessages = 0, 
                // used in SMTP Hello command
                ClientDomain = "mail.example.com"  
            }
        }
    }
};

Settings can be saved and restored:

JavaScript
var settings = Settings.Deserialize("MailMergeLib.config");
settings.Serialize("MailMergeLib.config");

Event Handlers

MailMergeSender allows for custom event handlers. MailMergeSender will raise events OnBeforeSend, OnAfterSend, OnSendFailure, OnMergeBegin, OnMergeComplete, and OnMergeProgress. Example:

JavaScript
var mms = new MailMergeSender {Config = settings.SenderConfig};
mms.OnAfterSend += (sender, args) => { // do something useful here };

Create a New Message

The Data Source for a Mail Message

Of course, the data source is the most important topic for mail merging.

First, let's create a Dictionary which will contain the values for {placeholders} in the message. Placeholders are the names in curly braces of e.g. IList item properties, or the column names of a data table.

JavaScript
var variables = new Dictionary<string, object>() 
{ { "Email", "sample@example.com" }, {"Name", "John Specimen"} };

{placeholders} can be used in email addresses, in the subject, in the plain text or HTML body, in text attachment content and in attachment names (including embedded images of the HTML body).

So in case of 2 recipients, you could create an anonymous type:

JavaScript
var variables = new[]
     {
        new {Email = "sample1@example.com", Name = "John Specimen"}
        new {Email = "sample2@example.com", Name = "Mary Specimen"}
     };

When using O/R mappers, an entity field could look like this: Department.Leader.FirstName. So you could use the field name as a placeholder with "dot notation": {Department.Leader.FirstName}.

Message Body

Next, we create a new message with a subject, plain text and an HTML body part.

JavaScript
var mmm = new MailMergeMessage("Personal subject for {Name}", "This is pure text for {Name}",
            "<html><head><title>No title</title></head>
            <body>This is HTML text for {Name}</body></html>") {Config = settings.MessageConfig};

In case the HTML part contains an image, MailMergeLib will automatically add it to the email as a "linked resource". The image source must contain the path to a local file. The path may contain {placeholders} as well.

HTML
<img src="file:///full-path-to-your-image-file.jpeg" alt="Image" width="100" height=100"/>

Instead of supplying the full path for each image, it's possible to set the path for images:

JavaScript
mmm.Config.FileBaseDirectory = "Path-to-images";

You want to see how the final message will look like? Just save it to a file and explore it with a text editor or your email application (e.g. Microsoft Outlook or Mozilla Thunderbird):

JavaScript
mmm.GetMimeMessage(variables).WriteTo("enter-your-filename-here.eml");

If the plain text part should not be supplied manually, MailMergeLib can do the work to convert HTML to plain text:

JavaScript
mmm.PlainText = mmm.ConvertHtmlToPlainText();

To do this job, MailMergeMessage supplies the ParsingHtmlConverter for converting HTML to plain text. It uses the open source library AngleSharp^ and has a similar approach like Markdown^ by John Gruber. It converts the most common HTML tags and attributes which appear in emails, not on web pages, to plain text with quite good results. If AngleSharp is not available or fails, the very basic RegExHtmlConverter is used (similar to this solution^), unless you provide your custom converter implementing IHtmlConverter.

Formatting Capabilities

The placeholders and the formatting capabilities allows separation of code and design. In order to change the content of an email, just change the template file(s) for the plain or HTML text and you're done without a need to touch your code.

Generally, formatting of {placeholders} is fully compatible with string.Format by means of SmartFormat.Net which is built into MailMergeLib. For details, please refer to the SmartFormat Wiki. SmartFormat.Net is exposed as the SmartFormatter property of a MailMergeMessage.

Here are some examples which have an equivalent to string.Format:

JavaScript
"{Date:yyy-MM-dd}"  for a variable of type DateTime
"You are visitor number {NumOfVisitors}"  for a variable of type int
"{Name} from {Address.City}, {Address.State}"  for a variable which is an instance of class "user"

Formatting extensions coming with SmartFormat.Net:

JavaScript
"You have {emails.Count} new {emails.Count:message|messages}"  // pluralization
"{Users:{Name}|, |, and } liked your comment"  // a list of users
"{Name:choose(null|):N/A|empty|{Name}}"        // assuming an object with property "Name" 
                                               // which can be null, empty or a string

Attachments

You may also want to add some individualized attachments by adding placeholders to the file name. E.g.:

JavaScript
mmm.FileAttachments.Add
    (new FileAttachment("testmail_{Username}.pdf", "sample.pdf", "application/pdf"));

And that's the way to add string attachments:

JavaScript
mmm.StringAttachments.Add(new StringAttachment
    ("Some programmatically created content", "logfile.txt", "text/plain"));

Mail Addresses

For sending a mail, we need to supply at least one recipient's address and the sender's address. Again, using placeholders makes it possible to create individualized emails.

JavaScript
mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, "whatever@example.com"));
mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));

Miscellaneous

If you're using a data source with variables, it may well happen that you have recipients with empty e-mail fields. That's why you may want to not throw an exception with empty addresses.

JavaScript
mmm.Config.IgnoreEmptyRecipientAddr = true;

Want to change MailMergeLib's identification? Set the mail's Xmailer header to anything you like.

JavaScript
mmm.Config.Xmailer = "MailMergeLib 5.0";

Sending a Message

Using the MailMergeSender is quite straight forward: Create instance of the class and provide some settings explained in the Configuration chapter.

Creating a configured MailMergeSender

Setup the mail sender:

JavaScript
var mailSender = new MailMergeSender {Config = settings.SenderConfig};

Start the Transfer

For the Send job, there are several alternatives:

  1. Send a single message as an asynchronous operation:
    JavaScript
    var dict = new Dictionary<string, object>() 
    { { "Email", "sample@example.com" }, {"Name", "John Specimen"} };
    mailSender.SendAsync(mmm, (object) dict);
  2. Send all messages for all items in the DataSource as an asynchronous operation:
    JavaScript
    var variables = new[]
       {
         new {Email = "sample1@example.com", Name = "John Specimen"}
         new {Email = "sample2@example.com", Name = "Mary Specimen"}
       };
     mailSender.SendAsync(mmm, variables);
  3. Send messages as a synchronous operation for all items in the DataSource:
    JavaScript
    mailSender.Send(mmm, variables);
  4. Or just send a single message providing a Dictionary with name/value pairs synchronously:
    JavaScript
    mailSender.Send(mmm, (object) dict);

SMTP Fail-over Configuration

When you define more than one SMTP configurations in the SenderConfig, then the first SMTP configuration will be the standard one used to send messages. The second one will be used in case sending over the standard configuration will fail.

This can be extremely useful in unattended environments (e.g. a scheduled task on a web server).

JavaScript
SenderConfig =
{
    SmtpClientConfig = new[]
    {
        new SmtpClientConfig()
        {
            MessageOutput = MessageOutput.SmtpServer,
            SmtpHost = "some.host.com",
            SmtpPort = 25,
            NetworkCredential = new Credential("user", "password"),
            Name = "Standard Config",
            MaxFailures = 3,
            DelayBetweenMessages = 500
        },
        new SmtpClientConfig()
        {
            MessageOutput = MessageOutput.SmtpServer,
            SmtpHost = "some.otherhost.com",
            SmtpPort = 587,
            NetworkCredential = new Credential("user2", "password2"),
            Name = "Backup Config",
            DelayBetweenMessages = 1000
        }
    },
    MaxNumOfSmtpClients = 5
}

Using More than One SmtpClient for Sending Messages

Setting mailSender.Config.MaxNumOfSmtpClients = 5 will use five parallel SmtpClients for sending messages asynchronously.

When defining more than one SMTP configuration, each of the configurations will be assigned alternating to the SmtpClients. Fail-over will also work slightly differently: in case of failure, the first configuration other than the current one will be used.

Cancelling a Send Operation

Asynchronous Send operations can be cancelled at any time:

JavaScript
mailSender.SendCancel();

Influencing Error Handling

Timeout in milliseconds:

JavaScript
mailSender.Config.Timeout = 100000;

Maximum number of failures until sending a message will finally fail:

JavaScript
mailSender.Config.MaxFailures = 3;

Retry delay time between failures:

JavaScript
mailSender.Config.RetryDelayTime = 3000;

Delay time between each message:

JavaScript
mailSender.Config.DelayBetweenMessages = 1000;

Conclusion

The new version of MailMergeLib is using MimeKit and MailKit. They are excellent open source libraries which give a very fine grained control over the whole process of email message generation and distribution. And last but not the least, they produce RFC standards compliant email messages.

It is recommended to migrate from former versions to MailMergeLib version 5.

Special Thanks To

  • Jeffrey Stedfast for his MimeKit and MailKit open source libraries
  • Florian Rappl for his open source AngleSharp HTML parser
  • John Gruber for sharing his Markdown xslt-based text-to-HTML conversion tool
  • All users giving their feedback and votes in the forum

Releases

  • 2007-07-10: Initial public release MailMergeLib 2.0 using System.Net.Mail
  • 2009-12-23: MailMergeLib 3.0 using System.Net.Mail
  • 2010-05-01: MailMergeLib 4.0 using System.Net.Mail
  • 2016-09-04: MailMergeLib 5.0 - a major rewrite using MimeKit and MailKit for low level operations
  • 2016-10-23: Since MailMergeLib 5.1.0 also supporting .NET Core

The latest version of MailMergeLib is available on GitHub: https://github.com/axuno/MailMergeLib

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Problem with html mail Pin
axuno22-May-10 0:00
axuno22-May-10 0:00 
AnswerRe: Problem with html mail Pin
Gismow25-May-10 0:22
Gismow25-May-10 0:22 
AnswerProblem with html mail: Resolved Pin
axuno7-Jun-10 11:22
axuno7-Jun-10 11:22 
Generalcompile error with Visual Studio 2005 Pin
SaschaGottfried11-May-10 4:30
SaschaGottfried11-May-10 4:30 
GeneralNo more Visual Studio 2005 support Pin
axuno12-May-10 6:03
axuno12-May-10 6:03 
QuestionSystem.Net.Mail.MailMessage - return-path problem, is it solved ? Pin
morSchmidt4-May-10 0:27
morSchmidt4-May-10 0:27 
AnswerRe: System.Net.Mail.MailMessage - return-path problem, is it solved ? Pin
axuno4-May-10 12:06
axuno4-May-10 12:06 
GeneralRe: System.Net.Mail.MailMessage - return-path problem, is it solved ? Pin
morSchmidt7-May-10 3:57
morSchmidt7-May-10 3:57 
Hi thanks for the answer.

However our server is hosted where we cannot use the internal IIS smtp.
We have an external smtp server which we use for this.

Performance is no problem because we simply queue the messages in an SQL server and then a Windows Service takes care of sending them to the SMTP server.

But you are saying that you have no idea how to hack this ?
And that if i tried using MailMergeLib i would have the exact same issue ?
AnswerRe: System.Net.Mail.MailMessage - return-path problem, is it solved ? Pin
axuno7-May-10 4:40
axuno7-May-10 4:40 
GeneralRe: System.Net.Mail.MailMessage - return-path problem, is it solved ? Pin
morSchmidt13-May-10 3:28
morSchmidt13-May-10 3:28 
GeneralNot send many email address Pin
tuanpm27-Apr-10 17:08
tuanpm27-Apr-10 17:08 
GeneralRe: Not send many email address Pin
axuno30-Apr-10 6:14
axuno30-Apr-10 6:14 
Generalgetting MailMergeLib.MailMergeMessage+MailMergeMessageException: Building of message failed Pin
ravikumarmp8-Apr-10 22:07
ravikumarmp8-Apr-10 22:07 
AnswerRe: getting MailMergeLib.MailMergeMessage+MailMergeMessageException: Building of message failed Pin
axuno30-Apr-10 6:23
axuno30-Apr-10 6:23 
GeneralSend pdf attachtment from Memorystream Pin
Annet Vink17-Mar-10 6:12
Annet Vink17-Mar-10 6:12 
GeneralRe: Send pdf attachtment from Memorystream Pin
axuno17-Mar-10 11:52
axuno17-Mar-10 11:52 
GeneralStrange System.Net.Mail.SmtpException with some mails using QuotedPrintableRFC2405 htmlBody [modified] Pin
jfoucher11-Mar-10 0:10
jfoucher11-Mar-10 0:10 
GeneralConfirmed: Strange System.Net.Mail.SmtpException due to anti-virus software Pin
axuno11-Mar-10 9:12
axuno11-Mar-10 9:12 
News.NET 4.0 RC Pin
axuno13-Feb-10 5:31
axuno13-Feb-10 5:31 
GeneralTwo questions Pin
Member 7274074-Feb-10 11:49
Member 7274074-Feb-10 11:49 
AnswerRe: Two questions Pin
axuno5-Feb-10 7:58
axuno5-Feb-10 7:58 
GeneralBug in MailMergeSender.Send(mailMergeMessage, isAsync) Pin
boulix4-Feb-10 3:31
boulix4-Feb-10 3:31 
AnswerRe: Bug in MailMergeSender.Send(mailMergeMessage, isAsync) Pin
axuno4-Feb-10 9:12
axuno4-Feb-10 9:12 
GeneralRe: Bug in MailMergeSender.Send(mailMergeMessage, isAsync) Pin
Member 7274074-Feb-10 9:57
Member 7274074-Feb-10 9:57 
AnswerRe: Bug in MailMergeSender.Send(mailMergeMessage, isAsync) Pin
axuno4-Feb-10 10:00
axuno4-Feb-10 10:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.