Mail merge and bulk mail


Mail merge in MailBee.NET

Mail merge is a process of generating a series of e-mail messages based on a single template and a data source. The data source (database table, SQL query and so on) delivers the data rows which are applied to the template to produce e-mail messages.

In most cases, you'll use mail merge over database so that the names of data table columns will be used as pattern names. Thus, if the message body contains ##item_name## and there is the data table column named item_name, MailBee.NET will replace ##item_name## in the message body with the value in the data table denoted by the current row and the column named item_name. Both plain-text and HTML bodies can contain patterns.

You can use patterns not only in the body and headers but also in attachment filenames, Delivery Status Notification (DSN) settings, e-mail addresses of actual recipients and sender (if it's different from From address). This guide covers most of these matters. Using templates in DSN is explained in another guide, see Fine-tune ESMTP DSN and assign Tracking ID to e-mail topic.

Also, MailBee.NET provides the mechanism of implementing mail merge manually. It allows for a greater control over the mail merge process and can be used if the data source cannot be represented as aSystem.Data.DataTable or System.Data.IDataReader object. To learn more, refer to MailMerge object documentation.

The e-mails generated with mail merge can be sent immediately or submitted to a mail queue like Microsoft IIS SMTP Pickup service or MailBee.NET Queue.

The mail merge mechanism can generate and send unlimited number of e-mails even the size of RAM memory of the server does not allow to fit all the e-mails generated during mail merge. This is because mail merge occurs in a memory conservative way: the data row gets merged with the template, the e-mail is built, sent and then freed when it comes to the next data row. In multi-threaded mode, the number of e-mails being simultaneously in memory is the number of active threads but it's still limited.


Bulk mail, explained

Bulk mail denotes sending large volumes of e-mails. Although the source of bulk mail is usually the messages generated with mail merge (mail merge can produce thousands of individual e-mails), you can also use bulk mail facilities of MailBee.NET to send e-mails crafted manually.

In MailBee.NET, the source of bulk mail is jobs (SendMailJob objects). The job represents a task of sending a single e-mail or mail merge series. For instance, when you call Smtp.SendMailMerge method to create and send a series of e-mails with mail merge, MailBee.NET actually creates a SendMailJob of mail merge type and executes it.

This guide uses bulk mail only in conjunction with mail merge. For examples on how to send lots of individual e-mails not using mail merge, refer to Send multiple individual e-mails or send through multiple servers guide.


Error processing in bulk mail operations

The error processing in case of bulk mail operations (including mail merge) is different as compared to sending a single e-mail.

If you send a single e-mail and it fails, that's an error, no question here, and MailBee.NET throws an exception then. But in case of multiple e-mails being sent, failure of a single e-mail shouldn't always stop the entire bulk mail operation.

MailBee.NET lets your application stay informed about the failures of individual e-mails without interrupting the whole process by using events. See Monitor mail merge progress and track successful and failed sendings topic for details.


Send series of e-mails based on template and database

This console sample creates the data table containing two rows, and performs mail merge over it. Note that the mail merge process also involves attaching a file which name is a pattern (so that it will attach different files for different data rows):

using System;
using System.Data;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Sample
{
    // 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;
    }

    static void Main(string[] args)
    {
        Smtp mailer = new Smtp();

        // Because mail merge does not throw exceptions, you need to
        // subscribe to ErrorOccurred event or check the log for errors.
        mailer.Log.Enabled = true;
        mailer.Log.Filename = @"C:\Temp\log.txt";
        mailer.Log.Clear();

        // 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.BodyHtmlText = "Attachment for <i>##name##</i>";

        // You may comment this line out if you don't need attacments.
        // Otherwise, you'll need to add "annual report.xls" and
        // "agreement draft.pdf" into "C:\Docs" folder.
        mailer.Message.Merge.AddAttachmentPattern(@"C:\Docs\##doc_name##");

        // Generate and send e-mails.
        mailer.SendMailMerge(null, null, GetData());
    }
}
Imports System
Imports System.Data
Imports MailBee
Imports MailBee.Mime
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

    Sub Main()
        Dim mailer As New Smtp()

        ' Because mail merge does not throw exceptions, you need to
        ' subscribe to ErrorOccurred event or check the log for errors.
        mailer.Log.Enabled = True
        mailer.Log.Filename = "C:\Temp\log.txt"
        mailer.Log.Clear()

        ' 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.BodyHtmlText = "Attachment for <i>##name##</i>"

        ' You may comment this line out if you don't need attacments.
        ' Otherwise, you'll need to add "annual report.xls" and
        ' "agreement draft.pdf" into "C:\Docs" folder.
        mailer.Message.Merge.AddAttachmentPattern("C:\Docs\##doc_name##")

        ' Generate and send e-mails.
        mailer.SendMailMerge(Nothing, Nothing, GetData())
    End Sub
End Module

Here and below, we assume that MailBee.NET SMTP license key is already set in app.config, web.config or Windows Registry. See Import namespaces and set license key topic for details.

Also, the sample needs annual report.xls and agreement draft.pdf files in C:\Docs folder. If the files are not there, you won't see any exception, the e-mails will just not be sent. Refer to Monitor mail merge progress and track successful and failed sendings topic to learn why.

There is also an async version of this sample in Send multiple e-mails asynchronously topic.


Mail merge with post processing

You can use events to post-process the e-mail message after it has been generated with mail merge but before it gets sent. For instance, if you want to sign it with DKIM or S/MIME signature (see "Other notes" section in Sign e-mail with DomainKeys and DKIM topic to get the idea).

The sample below differs from the previous sample in one thing: the attachment filename on disk is different from the attachment filename as it must appear in the message (for instance, if you're attaching temp files which don't have meaningful names on the filesystem). MailMerge.AddAttachmentPattern method won't work in this case as it does not allow you to specify different "real" and "target" filenames. That's how you can overcome this with events:

using System;
using System.Data;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Sample
{
    // 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;
    }

    // After mail merge produced the message, we manually attach a file
    // as we need it to have different "real" (on the filesystem) and
    // "target" (in the e-mail) filenames.
    static void OnSending(object sender, SmtpSendingMessageEventArgs e)
    {
        e.MailMessage.Attachments.Add(@"C:\Docs\" +
            e.MergeTable.Rows[e.MergeRowIndex]["doc_id"].ToString() + ".tmp",
            e.MergeTable.Rows[e.MergeRowIndex]["doc_name"].ToString());
    }

    static void Main(string[] args)
    {
        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 and body.
        mailer.Message.To.AsString = "##name## <##email##>";
        mailer.Message.BodyHtmlText = "Attachment for <i>##name##</i>";

        // Subscribe to the event which raises each time
        // the message is about to be sent.
        mailer.SendingMessage +=
            new SmtpSendingMessageEventHandler(OnSending);

        // Generate and send e-mails.
        mailer.SendMailMerge(null, null, GetData());
    }
}
Imports System
Imports System.Data
Imports MailBee
Imports MailBee.Mime
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.
    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

    ' After mail merge produced the message, we manually attach a file
    ' as we need it to have different "real" (on the filesystem) and
    ' "target" (in the e-mail) filenames.
    Sub OnSending(ByVal sender As Object, ByVal e As SmtpSendingMessageEventArgs)
        e.MailMessage.Attachments.Add("C:\Docs\" & _
            e.MergeTable.Rows(e.MergeRowIndex)("doc_id").ToString() & ".tmp", _
            e.MergeTable.Rows(e.MergeRowIndex)("doc_name").ToString())
    End Sub

    Sub Main()
        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 and body.
        mailer.Message.To.AsString = "##name## <##email##>"
        mailer.Message.BodyHtmlText = "Attachment for <i>##name##</i>"

        ' Subscribe to the event which raises each time
        ' the message is about to be sent.
        AddHandler mailer.SendingMessage, AddressOf OnSending

        ' Generate and send e-mails.
        mailer.SendMailMerge(Nothing, Nothing, GetData())
    End Sub
End Module

As you can see, the sample needs 637.tmp and 2358.tmp files in C:\Docs folder.

Note that if OnSending method throws an exception (for instance, the file with the given filename was not found or whatever), MailBee.NET will catch it. See below why, and how to deal with errors and exceptions in case of sending bulk mail.


Monitor mail merge progress and track successful and failed sendings

Although you can set Smtp.StopJobsOnError to true in case if you want bulk mail operation to stop as soon as any e-mail in the bulk fails, you'll mostly use this for debugging. In production, it's not convenient to have the whole process to stop in case of failure of a single e-mail. Still, you need to somehow track the failed e-mails and the reasons of the occurred errors.

During the bulk mail operation, you can use Smtp.ErrorOccurred and Smtp.MessageNotSent events to get notified of any problems.

After the bulk mail operation, you can examine Smtp.JobsFailed collection to find out which e-mails haven't been processed. The exact approach of using Smtp.JobsFailed property depends on whether the bulk mail operation was a mail merge or a batched sending of independent e-mails, but the key concept remains the same.

The sample below is the more advanced version of the previous sample (mail merge of attachments having different filenames on disk and in e-mail), with the extensive error handling:

using System;
using System.Data;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Sample
{
    // 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;
    }

    // After mail merge produced the message, we manually attach a file
    // as we need it to have different "real" (on the filesystem) and
    // "target" (in the e-mail) filenames.
    static void OnSending(object sender, SmtpSendingMessageEventArgs e)
    {
        e.MailMessage.Attachments.Add(@"C:\Docs\" +
            e.MergeTable.Rows[e.MergeRowIndex]["doc_id"].ToString() + ".tmp",
            e.MergeTable.Rows[e.MergeRowIndex]["doc_name"].ToString());
    }

    // Raises in many cases. Does not necessarily indicates the e-mail wasn't
    // sent. For instance, if you're using main and fail-over backup SMTP servers
    // and the main server is down, you'll get OnError stating about that, but
    // then the message can still get sent through the fail-over server.
    static void OnError(object sender, ErrorEventArgs e)
    {
        if (e.Reason is MailBeeExternalException)
        {
            Console.WriteLine(
                "OnError: Some of your event handlers has thrown an exception.");
            Console.WriteLine("The details: " + e.Reason.Message);
            Console.WriteLine();
        }
        else if (e.Reason is MailBeeNetworkException)
        {
            // Do nothing because OnNotSent will take care of failed messages.
            // ErrorOccurred event is not so reliable when it comes to
            // MailBeeNetworkException which may occur multiple times with
            // fail-over servers and the e-mail can still get delivered.
            // Other, non-MailBeeNetworkException errors, are definitive and
            // non-recoverable. If a non-MailBeeNetworkException error has
            // occurred for a certain e-mail, you can count on that the
            // operation won't be retried and probably succeed for the second
            // time (like it's possible with MailBeeNetworkException and
            // fail-over servers).
        }
        else
        {
            Console.WriteLine("OnError: " + e.Reason.Message);
            Console.WriteLine();
        }

    }

    // Raises if the e-mail could not be sent. The e-mail must have been
    // successfully generated during mail merge or provided by the application.
    // Doesn't raise if the error occurred before sending (at mail merge stage).
    private static void OnNotSent(object sender, SmtpMessageNotSentEventArgs e)
    {
        Console.WriteLine("OnNotSent: Failed row #" + e.MergeRowIndex.ToString());
        Console.WriteLine("OnNotSent: " + e.Reason.Message);
        Console.WriteLine();
    }

    static void Main(string[] args)
    {
        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 and body.
        mailer.Message.To.AsString = "##name## <##email##>";
        mailer.Message.BodyPlainText = "Attachment for ##name##";

        // Raises each time the message is about to be sent. We use it
        // to add attachment after merging the template and data row.
        mailer.SendingMessage +=
            new SmtpSendingMessageEventHandler(OnSending);

        // Raises whenever anything goes wrong (warning or fatal error).
        // In our case, mails get generated with the help of external code
        // (in OnSending event handler). If that code fails, ErrorOccurred
        // is the only way to get the error description.
        mailer.ErrorOccurred += new ErrorEventHandler(OnError);

        // Raises when sending the message started but then failed.
        // However, does not raise if the message wasn't even built.
        // For instance, failed mail merge won't trigger it.
        mailer.MessageNotSent += new SmtpMessageNotSentEventHandler(OnNotSent);

        // Uncomment if you want to stop bulk mail procedure on any error.
        // mailer.StopJobsOnError = true;

        // Generate and send e-mails.
        mailer.SendMailMerge(null, null, GetData());
    }
}
Imports System
Imports System.Data
Imports MailBee
Imports MailBee.Mime
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.
    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

    ' After mail merge produced the message, we manually attach a file
    ' as we need it to have different "real" (on the filesystem) and
    ' "target" (in the e-mail) filenames.
    Sub OnSending(ByVal sender As Object, ByVal e As SmtpSendingMessageEventArgs)
        e.MailMessage.Attachments.Add("C:\Docs\" & _
            e.MergeTable.Rows(e.MergeRowIndex)("doc_id").ToString() & ".tmp", _
            e.MergeTable.Rows(e.MergeRowIndex)("doc_name").ToString())
    End Sub

    ' Raises in many cases. Does not necessarily indicates the e-mail wasn't
    ' sent. For instance, if you're using main and fail-over backup SMTP servers
    ' and the main server is down, you'll get OnError stating about that, but
    ' then the message can still get sent through the fail-over server.
    Sub OnError(ByVal sender As Object, ByVal e As ErrorEventArgs)
        If TypeOf e.Reason Is MailBeeExternalException Then
            Console.WriteLine(
                "OnError: Some of your event handlers has thrown an exception.")
            Console.WriteLine(("The details: " & e.Reason.Message))
            Console.WriteLine()
        Else
            If TypeOf e.Reason Is MailBeeNetworkException Then
                ' Do nothing because OnNotSent will take care of failed messages.
                ' ErrorOccurred event is not so reliable when it comes to
                ' MailBeeNetworkException which may occur multiple times with
                ' fail-over servers and the e-mail can still get delivered.
                ' Other, non-MailBeeNetworkException errors, are definitive and
                ' non-recoverable. If a non-MailBeeNetworkException error has
                ' occurred for a certain e-mail, you can count on that the
                ' operation won't be retried and probably succeed for the second
                ' time (like it's possible with MailBeeNetworkException and
                ' fail-over servers).
            Else
                Console.WriteLine(("OnError: " & e.Reason.Message))
                Console.WriteLine()
            End If
        End If
    End Sub

    ' Raises if the e-mail could not be sent. The e-mail must have been
    ' successfully generated during mail merge or provided by the application.
    ' Doesn't raise if the error occurred before sending (at mail merge stage).
    Sub OnNotSent(ByVal sender As Object, ByVal e As SmtpMessageNotSentEventArgs)
        Console.WriteLine(("OnNotSent: Failed row #" & e.MergeRowIndex.ToString()))
        Console.WriteLine(("OnNotSent: " & e.Reason.Message))
        Console.WriteLine()
    End Sub

    Sub Main()
        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 and body.
        mailer.Message.To.AsString = "##name## <##email##>"
        mailer.Message.BodyPlainText = "Attachment for ##name##"

        ' Raises each time the message is about to be sent. We use it
        ' to add attachment after merging the template and data row.
        AddHandler mailer.SendingMessage, AddressOf OnSending

        ' Raises whenever anything goes wrong (warning or fatal error).
        ' In our case, mails get generated with the help of external code
        ' (in OnSending event handler). If that code fails, ErrorOccurred
        ' is the only way to get the error description.
        AddHandler mailer.ErrorOccurred, AddressOf OnError

        ' Raises when sending the message started but then failed.
        ' However, does not raise if the message wasn't even built.
        ' For instance, failed mail merge won't trigger it.
        AddHandler mailer.MessageNotSent, AddressOf OnNotSent

        ' Uncomment if you want to stop bulk mail procedure on any error.
        ' mailer.StopJobsOnError = true;
        ' Generate and send e-mails.
        mailer.SendMailMerge(Nothing, Nothing, GetData())
    End Sub
End Module

Submit e-mails generated with mail merge to MailBee.NET Queue

Mail merge in MailBee.NET can also be used with a mail queue like MS IIS SMTP Pickup service or MailBee.NET Queue.

MailBee.NET Queue is a separate application from MailBee.NET Objects, it comes with its own installer. You, however, don't need a separate license key for it. MailBee.NET Queue consumes your existing MailBee.NET SMTP key (unified MailBee.NET Objects key will work either).

Several Visual Studio sample projects demonstrating how to submit e-mails to MailBee.NET Queue (including mail merge) are shipped with MailBee.NET Queue itself. Once installed MailBee.NET Queue, you can find them in Samples section of Start / Programs / MailBee.NET Queue menu.

In short, to perform bulk mailing into a mail queue rather than sending directly, use Smtp.AddJob and Smtp.SubmitJobsToPickupFolder methods instead of Smtp.SendMailMerge:

using System;
using System.Data;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Sample
{
    // 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;
    }

    static void Main(string[] args)
    {
        // Note that we do not add any SMTP relay server here.
        Smtp mailer = new Smtp();

        // 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##");

        // Create mail merge job.
        mailer.AddJob(null, null, null, GetData());

        // Generate e-mails accordingly the mail merge job and submit them
        // to MailBee.NET Queue (IIS SMTP Pickup service would work too).
        mailer.SubmitJobsToPickupFolder(
            "C:\\MailBeeNetQueue Files\\Pickup", false);
    }
}
Imports System
Imports System.Data
Imports MailBee
Imports MailBee.Mime
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.
    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

    Sub Main()
        ' Note that we do not add any SMTP relay server here.
        Dim mailer As New Smtp()

        ' 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##")

        ' Create mail merge job.
        mailer.AddJob(Nothing, Nothing, Nothing, GetData())

        ' Generate e-mails accordingly the mail merge job and submit them
        ' to MailBee.NET Queue (IIS SMTP Pickup service would work too).
        mailer.SubmitJobsToPickupFolder("C:\MailBeeNetQueue Files\Pickup", False)
    End Sub
End Module

Note that the error handling issue applies to this method too. In case if, for instance, the attachment under the given name is not found, MailBee.NET will just silently go to the next message, and so on. Use Smtp.ErrorOccurredevent, like in previous sample, for handling errors.

If you need to alter the message after mail merge, use Smtp.SubmittingMessageToPickupFolder event like you used Smtp.SendingMessage in case of "normal send".


Send feedback to AfterLogic

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