SmtpAddJob Method (String, String, EmailAddressCollection, IDataReader, Boolean, Boolean)
Puts a "mail merge over data reader" job onto waiting list for subsequent processing in bulk mode.

Namespace: MailBee.SmtpMail
Assembly: MailBee.NET (in MailBee.NET.dll) Version: 12.3.1 build 666 for .NET 4.5
public void AddJob(
	string tag,
	string senderEmailPattern,
	EmailAddressCollection recipientsPattern,
	IDataReader mergeDataReader,
	bool keepProducedJobs,
	bool keepMergedData


Type: SystemString
Any string the developer wants to assign to Tag property of SendMailJob object created by this method. The developer can leave it a null reference (Nothing in Visual Basic).
Type: SystemString
The e-mail address template of the sender. If it's a null reference (Nothing in Visual Basic), the e-mail address template will be taken from From property.
Type: MailBee.MimeEmailAddressCollection
The e-mail address template of the recipients list. If it's a null reference (Nothing in Visual Basic), the recipients list will be constructed via merge of To, Cc, and Bcc patterns with actual values from the data source.
Type: System.DataIDataReader
The data source for mail merge.
Type: SystemBoolean
Indicates whether MailBee should create separate SendMailJob object for every e-mail message produced during mail merge. If true, JobsSuccessful and JobsFailed collections will end up with the same number of jobs as the number of e-mail messages generated; otherwise, only SendMailJob object originally added by this method will end up in JobsSuccessful or JobsFailed collections.
Type: SystemBoolean
Indicates whether MailBee should retain merged messages after they have been processed. If true, MailBee will not clear MergedXXX properties of SendMailJob object after the e-mail message associated with this object has been processed; otherwise, MailBee will clear memory occupied by the merged message so that MergedXXX properties start returning null once the message has been processed.
MailBeeInvalidArgumentExceptionmergeDataReader is a null reference (Nothing in Visual Basic).

This method is IDataReader version of AddJob(String, String, EmailAddressCollection, DataTable, Object, Boolean, Boolean) overload (which uses DataTable).

The specifics of IDataReader version is that there is no row indices to pass (the entire data reader is processed) because data readers don't have an index of a data row, they are not tables at all but rather sequential readers which can just move forward. Also, in multi-threaded scenario it's not possible to access the current row of the data reader in event handlers. See the sample code below on how to process only certain indices when dealing with IDataReader and how to overcome its multi-threading limitations.

Note Note
This method is not available in .NET Core and UWP editions.

This console sample is similar to the sample in AddJob(String, String, EmailAddressCollection, DataTable, Object, Boolean, Boolean) overload topic mentioned above, with specifics of using IDataReader in multi-threaded case. Also, the sample uses MS SQL Server database (SqlDataReader) rather than MS Access.

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

class Sample
    static ArrayList failedIds = new ArrayList();
    static int i = 0;

    // We use it to check if the data row with certain ID must be processed or not.
    static void mailer_MergingMessage(object sender, SmtpMergingMessageEventArgs e)
        // If this is 2nd run (retrying failed indices) and the index is not in failed list, skip it.
        int rowID = (int)e.MergeDataReaderRowValues[0];
        if (i > 0 && failedIds.IndexOf(rowID) < 0)
            e.MergeIt = false;

    // Reports successful attempt of sending e-mail.
    static void mailer_MessageSent(object sender, SmtpMessageSentEventArgs e)
        // Display e-mail address of the successful e-mail.
        // Demonstrate getting column by name.
        string email = e.MergeDataReaderRowValues[Array.IndexOf(e.MergeDataReaderColumnNames, "Email")].ToString();
        Console.WriteLine(string.Format("{0} of '{1}' job SUCCEEDED", email, e.Tag));

    // Reports failed attempt of sending e-mail.
    static void mailer_MessageNotSent(object sender, SmtpMessageNotSentEventArgs e)
        // Display e-mail address of the failed e-mail.
        // Demonstrate getting column by index (we assume field #0 is ID, field #1 is Email, etc).
        string email = e.MergeDataReaderRowValues[1].ToString();
        Console.WriteLine(string.Format("{0} of '{1}' job FAILED", email, e.Tag));

        // On the first run, remember failed id so that we'd be able to retry them on second run.
        if (i == 0)

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

        // Logging into a file is useful for troubleshooting.
        mailer.Log.Filename = @"C:\Temp\log.txt";
        mailer.Log.Enabled = true;
        mailer.Log.Format = LogFormatOptions.AddContextInfo;

        // Uncomment the line below to use unlimited number of worker threads (up to 60) and increase performance.
        // Note that not all SMTP servers will support so many incoming connections (each thread creates its own).

        // mailer.MaxThreadCount = -1;

        // Subscribe to the event which lets us implement retrying failed indices on 2nd run.
        mailer.MergingMessage += new SmtpMergingMessageEventHandler(mailer_MergingMessage);

        // Subscribe to events to track send bulk mail progress.
        mailer.MessageSent += new SmtpMessageSentEventHandler(mailer_MessageSent);
        mailer.MessageNotSent += new SmtpMessageNotSentEventHandler(mailer_MessageNotSent);

        // Setup SMTP server parameters.
        mailer.SmtpServers.Add("", "", "secret");

        // Setup e-mail message header template for mail merge.
        mailer.Message.From.AsString = "John Doe <>";
        mailer.Message.To.AsString = "##Name## <##Email##>";
        mailer.Message.Subject = "Our Oct/2013 newsletter";

        // Setup DSN template for mail merge. In particular, this can be useful
        // to track bounced messages which may come back from some addresses after
        // sending bulk mail out. If the SMTP server does not support DSN, this
        // setting will be ignored.
        mailer.DeliveryNotification.TrackingID = "Oct2013_##ID##";

        // Setup HTML body template.
        mailer.Message.BodyHtmlText = "<html>##Body##</html>";

        // Setup template for adding file attachments upon the specified path.
        // In this sample, the path to attachment files will be constructed as
        // "C:\Temp\" + DatabaseRecordField("Doc_path").

        // Make outgoing e-mails UTF-8 to allow content in any language.
        mailer.Message.Charset = "UTF-8";

        // Tell MailBee to generate alternative plain-text version
        // of each e-mail automatically.
        mailer.Message.Builder.HtmlToPlainMode = HtmlToPlainAutoConvert.IfHtml;

        // Specify database connection string (it may be different in your case).
        string connParams = @"server=(local)\SQLEXPRESS;Initial Catalog=afterlogic;Integrated Security=True";

        // Connect to the database.
        SqlDataReader reader = null;
        SqlConnection conn = new SqlConnection(connParams);
        SqlCommand command = conn.CreateCommand();

        // The 'mailing_list' table must have these fields:
        // #0: ID (int)
        // #1: Email (nvarchar)
        // #2: Name (nvarchar)
        // #3: Body (nvarchar)
        // #4; Doc_path (nvarchar). Can be null if attachment not required. Files are taken from C:\Temp.
        // Table row example: 196, "", "John", "Some text", "file.dat".
        command.CommandText = "SELECT * FROM mailing_list";

        // Make two runs of mail merge. If e-mails created from some data rows fail,
        // we'll attempt to resend them on the second run.
        for (i = 0; i < 2; i++)
            // Populate mail merge job to-do list with from the specified table.
            reader = command.ExecuteReader();

            // Create a job which is the following task for MailBee: perform mail merge over
            // the specified data table and send out each resulting e-mail to
            // the recipients which appear in the resulting messages. ""
            // address will be used as Return-Path (i.e. sender e-mail address).
            // keepProducedJobs is true to let us check mailer.JobsSuccessful.Count and
            // mailer.JobsFailed.Count at the end of the loop. In real apps, you can count successful
            // and failed sendings by incrementing variables in mailer_MessageSent or mailer_MessageNotSent
            // event handlers (this will save memory as produced jobs won't increase memory usage over time).
            // Just be sure to synchronize access to that counter variables (such as with lock or VB's SyncLock
            // statement if you're using multi-threading), unless you're in WinForms application which processes
            // events sequentially so that two event handlers could never execute simultaneously.
            mailer.AddJob("My", "", null, reader, true, false);

            // Run the job. The actual mail merge takes place here.

            reader = null;


            // Report results (row indices in the data table) to the console.
            if (mailer.JobsFailed.Count == 0)
                Console.WriteLine("All of the rows of the table have been processed and sent as e-mails.");
                if (mailer.JobsSuccessful.Count == 0)
                    Console.WriteLine("None of the rows of the table has been processed and sent as e-mail.");
                    Console.WriteLine("Not all rows of the table have been processed and sent as e-mails.");

                    Console.WriteLine("Successful rows count: " + mailer.JobsSuccessful.Count.ToString());

                    Console.WriteLine("Failed rows count: " + mailer.JobsFailed.Count.ToString());


        // In real apps, the statements below should be in 'finally' section to make sure
        // they execute even in case of exception.
See Also