Click here to Skip to main content
12,548,135 members (48,852 online)
Click here to Skip to main content
Add your own
alternative version


507 bookmarked

MailMergeLib - A .NET Mail Client Library

, 8 Oct 2016 MIT
Rate this:
Please Sign up or sign in to vote.
MailMergeLib is an SMTP template mail client library written in C# which provides comfortable mail merge capabilities and SMTP fail-over features.

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.


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 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.


  • 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


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:

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

A rather minimal configuration looks like this:

var settings = new Settings
    MessageConfig =
        CharacterEncoding = Encoding.UTF8,
        StandardFromAddress = new MailboxAddress("sender name", ""),
        // 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 = "",
                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 = ""  

Settings can be saved and restored:

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

Event handlers

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

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.

var variables = new Dictionary<string, object>() { { "Email", "" }, {"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:

var variables = new[]
        new {Email = "", Name = "John Specimen"}
        new {Email = "", 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 a HTML body part.

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.

<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: 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):


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

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:

"{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:

"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


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

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

And that's the way to add string attachments:

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

Mail Addresses

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

mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.From, ""));
mmm.MailMergeAddresses.Add(new MailMergeAddress(MailAddressType.To, "{Name}", "{Email}"));


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.

mmm.Config.IgnoreEmptyRecipientAddr = true;

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

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:

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

SenderConfig =
    SmtpClientConfig = new[]
        new SmtpClientConfig()
            MessageOutput = MessageOutput.SmtpServer,
            SmtpHost = "",
            SmtpPort = 25,
            NetworkCredential = new Credential("user", "password"),
            Name = "Standard Config",
            MaxFailures = 3,
            DelayBetweenMessages = 500
        new SmtpClientConfig()
            MessageOutput = MessageOutput.SmtpServer,
            SmtpHost = "",
            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:


Influencing Error Handling

Timeout in milliseconds:

mailSender.Config.Timeout = 100000;

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

mailSender.Config.MaxFailures = 3;

Retry delay time between failures:

mailSender.Config.RetryDelayTime = 3000;

Delay time between each message:

mailSender.Config.DelayBetweenMessages = 1000;


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 not 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


  • 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

The latest version of MailMergeLib is available on GitHub:


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


About the Author

Germany Germany
No Biography provided

You may also be interested in...


Comments and Discussions

QuestionHow would I BCC to multiple addresses? Pin
Mvet20-Aug-13 0:23
memberMvet20-Aug-13 0:23 
AnswerRe: How would I BCC to multiple addresses? Pin
Norbert Bietsch20-Aug-13 10:37
memberNorbert Bietsch20-Aug-13 10:37 
GeneralMy vote of 5 Pin
Prasad Khandekar28-Jul-13 20:50
professionalPrasad Khandekar28-Jul-13 20:50 
QuestionNET 4.5 and NuGet Pin
Tiago Freitas Leal14-May-13 11:51
memberTiago Freitas Leal14-May-13 11:51 
Questionmail form Pin
cursore8411-Apr-13 22:38
membercursore8411-Apr-13 22:38 
QuestionSending via Google Mail and TLS Pin
Garry Lowther2-Apr-13 1:55
memberGarry Lowther2-Apr-13 1:55 
AnswerRe: Sending via Google Mail and TLS Pin
Norbert Bietsch2-Apr-13 6:20
memberNorbert Bietsch2-Apr-13 6:20 
GeneralRe: Sending via Google Mail and TLS Pin
Garry Lowther2-Apr-13 23:04
memberGarry Lowther2-Apr-13 23:04 
AnswerRe: Sending via Google Mail and TLS Pin
Norbert Bietsch3-Apr-13 8:43
memberNorbert Bietsch3-Apr-13 8:43 
GeneralRe: Sending via Google Mail and TLS Pin
Garry Lowther4-Apr-13 0:22
memberGarry Lowther4-Apr-13 0:22 
QuestionNuGet? Pin
Brian Sobel16-Mar-13 21:21
memberBrian Sobel16-Mar-13 21:21 
Generalthanks Pin
Denno.Secqtinstien6-Feb-13 20:35
memberDenno.Secqtinstien6-Feb-13 20:35 
GeneralMy vote of 5 Pin
csharpbd16-Nov-12 9:43
membercsharpbd16-Nov-12 9:43 
GeneralRe: My vote of 5 Pin
Norbert Bietsch16-Nov-12 11:18
memberNorbert Bietsch16-Nov-12 11:18 
Thanks a lot Blush | :O
SuggestionReturning the merged content Pin
dale_burrell2-Mar-12 11:32
memberdale_burrell2-Mar-12 11:32 
AnswerRe: Returning the merged content Pin
Norbert Bietsch2-Mar-12 11:53
memberNorbert Bietsch2-Mar-12 11:53 
SuggestionMaking the placeholder delimiters configurable Pin
dale_burrell2-Mar-12 11:26
memberdale_burrell2-Mar-12 11:26 
AnswerRe: Making the placeholder delimiters configurable Pin
Norbert Bietsch2-Mar-12 11:44
memberNorbert Bietsch2-Mar-12 11:44 
GeneralMy vote of 5 Pin
waaqee16-Feb-12 1:42
memberwaaqee16-Feb-12 1:42 
GeneralRe: My vote of 5 Pin
Norbert Bietsch16-Feb-12 8:31
memberNorbert Bietsch16-Feb-12 8:31 
GeneralRe: My vote of 5 Pin
waaqee16-Feb-12 19:36
memberwaaqee16-Feb-12 19:36 
GeneralReally nice approach Pin
Norbert Bietsch17-Feb-12 22:36
memberNorbert Bietsch17-Feb-12 22:36 
GeneralMy vote of 5 Pin
nz_ham23-Nov-11 11:30
membernz_ham23-Nov-11 11:30 
GeneralMy vote of 5 Pin
LeonardoX2214-Oct-11 2:22
memberLeonardoX2214-Oct-11 2:22 
Question1 Send or SendAsync operation at a time Pin
vicentillo7-Oct-11 0:35
membervicentillo7-Oct-11 0:35 
GeneralRe: 1 Send or SendAsync operation at a time Pin
Norbert Bietsch9-Oct-11 4:07
memberNorbert Bietsch9-Oct-11 4:07 
GeneralRe: 1 Send or SendAsync operation at a time Pin
vicentillo10-Oct-11 3:17
membervicentillo10-Oct-11 3:17 
GeneralThanks! Pin
toiyabe29-Jul-11 5:27
membertoiyabe29-Jul-11 5:27 
GeneralRe: Thanks! Pin
Norbert Bietsch29-Jul-11 8:31
memberNorbert Bietsch29-Jul-11 8:31 
Generalmissing file name when Includefile:"file" Pin
bquick15-Apr-11 3:07
memberbquick15-Apr-11 3:07 
AnswerRe: missing file name when Includefile:"file" Pin
Norbert Bietsch15-Apr-11 22:46
memberNorbert Bietsch15-Apr-11 22:46 
GeneralRe: missing file name when Includefile:"file" Pin
bquick18-Apr-11 3:08
memberbquick18-Apr-11 3:08 
GeneralSymantec Endpoint antivirus workaround [modified] Pin
Nikolay Unguzov4-Apr-11 1:03
memberNikolay Unguzov4-Apr-11 1:03 
Question"An invalid character was found in the mail header: 'с'." for .TransferEncoding Pin
Nikolay Unguzov23-Mar-11 8:16
memberNikolay Unguzov23-Mar-11 8:16 
QuestionRe: "An invalid character was found in the mail header: 'с'." for .TransferEncoding Pin
Norbert Bietsch27-Mar-11 7:12
memberNorbert Bietsch27-Mar-11 7:12 
AnswerRe: "An invalid character was found in the mail header: 'с'." for .TransferEncoding [modified] Pin
Nikolay Unguzov2-Apr-11 21:30
memberNikolay Unguzov2-Apr-11 21:30 
AnswerRe: "An invalid character was found in the mail header: 'с'." for .TransferEncoding Pin
Norbert Bietsch3-Apr-11 11:40
memberNorbert Bietsch3-Apr-11 11:40 
GeneralBuilding of message failed. No recipients error Pin
steve gould28-Dec-10 20:29
membersteve gould28-Dec-10 20:29 
AnswerRe: Building of message failed. No recipients error Pin
Norbert Bietsch29-Dec-10 11:22
memberNorbert Bietsch29-Dec-10 11:22 
GeneralRe: Building of message failed. No recipients error Pin
steve gould29-Dec-10 13:17
membersteve gould29-Dec-10 13:17 
Questioncan't send mail~~ Pin
VAete.Feng22-Dec-10 20:28
memberVAete.Feng22-Dec-10 20:28 
AnswerRe: can't send mail~~ Pin
Norbert Bietsch24-Dec-10 3:32
memberNorbert Bietsch24-Dec-10 3:32 
GeneralRe: can't send mail~~ Pin
VAete.Feng27-Dec-10 15:40
memberVAete.Feng27-Dec-10 15:40 
GeneralUnusual exception during load test Pin
Chris J20-Oct-10 10:04
memberChris J20-Oct-10 10:04 
AnswerRe: Unusual exception during load test Pin
Norbert Bietsch20-Oct-10 10:30
memberNorbert Bietsch20-Oct-10 10:30 
GeneralRe: Unusual exception during load test Pin
Chris J21-Oct-10 5:53
memberChris J21-Oct-10 5:53 
GeneralReading SMTP config from application config file Pin
Chris J18-Oct-10 11:16
memberChris J18-Oct-10 11:16 
AnswerRe: Reading SMTP config from application config file Pin
Norbert Bietsch19-Oct-10 7:04
memberNorbert Bietsch19-Oct-10 7:04 
QuestionCan I specify the MAIL FROM address? Pin
mikecarr14-Oct-10 8:37
membermikecarr14-Oct-10 8:37 
AnswerRe: Can I specify the MAIL FROM address? Pin
mikecarr14-Oct-10 8:40
membermikecarr14-Oct-10 8:40 

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.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.161021.1 | Last Updated 9 Oct 2016
Article Copyright 2007 by axuno
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid