![]() |
General Programming »
Internet / Network »
Email & SMTP
Beginner
License: The Code Project Open License (CPOL)
MailMergeLib - A .NET Mail Client LibraryBy Norbert BietschMailMergeLib is a mail client library. It makes use of .NET System.Net.Mail and provides comfortable mail merge capabilities. MailMergeLib corrects a number of the most annoying bugs and RFC violations that .NET 2.0 to .NET 3.5 suffer from. |
C# 2.0, C# 3.0.NET 2.0, Win2K, WinXP, Win2003, Vista, ASP.NET, VS2005, VS2008, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
During the time when I worked on .NET 1.1, I started to use the mail client DotNetOpenMail by Mike Bridge which really was doing a good job. Based on this, I built a library for doing basic mail merge in a Web service.
After moving to .NET 2.0, I thought that DotNetOpenMail had become obsolete because Microsoft had introduced their System.Net.Mail library. Today I still think that this is well designed code, but it has some annoying bugs and RFC violations - which I found out one after another, and which have not come to an end yet. Still the mail merge library grew and became quite comfortable to use.
With System.Net.Mail, I did not encounter any show stoppers. It will send messages.
The point is: some bugs and RFC violations will increase the spam rating of spam filters for your message, and some mail clients may show parts as garbage.
While working on System.Net.Mail, I found and fixed the following bugs:
MailMessage.MailAddressCollection.Clear does not clear the corresponding headers. If you want to re-use an existing MailMessage object for different recipients, you will have to remove the recipients headers yourself (e.g. MailMessage.Headers.Remove("to")). - fixed in .NET 2.0 SP1 to addresses. to addresses will never be encoded, no matter which Encoding parameter is used for a new MailAddress. - fixed in .NET 2.0 SP1, except that spaces are still not encoded. MailMessage.To.ToString() returns the encoded string only after the message was sent. According to the documentation, this should be the case no matter whether the message was already sent or not. MailMessage.Headers there is a bug where headers will have white space in an encoded text. This will lead to non-RFC 2047 compliant messages, which will increase the SPAM rating of the message. System.Net.Mail. TransferEncoding.SevenBit turns into a header text sevenbit, instead of 7bit. sevenbit is not RFC compliant and causes problems with some mail clients. - fixed in .NET 2.0 SP1 System.Net.Mail. RFC 2045 requires that Quoted-Printable encoding encodes lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, "soft" line breaks must be used. Although Microsoft has supplied some bug fixes, quite a few are not fixed up to .NET 3.5 SP1 (which includes .NET 2.0 SP1). So I tried to find ways to fix them on my own. All bugfixes are included in a static class Bugfixer which hopefully won't be needed in future versions of .NET. Pie in the Microsoft sky?
For sending a mail in System.Net.Mail, you'll first create a MailMessage, and then send it with SmtpClient. In MailMergeLib this is quite similar: you'll create a MailMergeMessage and then send it with MailMergeSender.
One big advantage of MailMergeLib comes from placeholders. {Placeholders} are the field names of a DataTable embedded in any text with curly braces.
So first create the message and adjust some settings. CultureInfo is relevant for formatting placeholders that contain dates, currency or numeric data.
// create the mail message
MailMergeMessage mmm = new MailMergeMessage("My subject for {Nickname}");
// adjust mail specific settings
mmm.CharacterEncoding = Encoding.GetEncoding("iso-8859-1");
mmm.CultureInfo = new System.Globalization.CultureInfo("en-US");
mmm.TextTransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;
mmm.BinaryTransferEncoding = System.Net.Mime.TransferEncoding.Base64;
It is possible to add standard .NET formatting attributes to your placeholders. For a date that will show the day number and the month's name, you would write: {Date:"{0:dd MMMM}"}.
In case a column of DataRow will have ExtendedProperties for null and/or format, these properties will be used. Examples:
myDataTable.Columns["Date"].ExtendedProperties.Add("format", "{0:F}");
myDataTable.Columns["Date"].ExtendedProperties.Add("null", "#Display for null#");
Of course column types and formatting attributes must fit each other.
The message body parts can be added or changed quite easily either by using them as a parameter in the constructor, or by setting the properties:
mmm.HtmlText = new System.IO.StreamReader("HtmlBody.html").ReadToEnd();
mmm.PlainText = new System.IO.StreamReader("TextBody.html").ReadToEnd();
HtmlText and PlainText can contain placeholders. You can insert a text file by using the special syntax {IncludeFile:"file"}. IncludeFile is the column name of the DataTable, while file means to interpret the content as a file name.
You may also want to add some personalized attachments by adding placeholders to the file name. E.g.:
mmm.FileAttachments.Add
(new FileAttachment("testmail_{Nickname}.pdf", "sample.pdf", "application/pdf"));
And that's the way to add string attachments:
mmm.StringAttachments.Add(new StringAttachment
("Some programmatically created content", "file.txt", "text/plain"));
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 personalized e-mails.
// add to and from addresses
mmm.MailMergeAddresses.Add
(new MailMergeAddress(MailAddressType.To, "<{Email}>", "{Nickname}"));
mmm.MailMergeAddresses.Add
(new MailMergeAddress(MailAddressType.From, "<from@addr.com>", "From Name"));
If your data comes from a DataTable, it may well happen that you have recipients with empty e-mail fields. That's why you may want to set:
mmm.IgnoreEmptyRecipientAddr = true
This will then not throw an exception with empty addresses.
Want to change MailMergeLib's identification? Set...
mmm.Xmailer = "MailMergeLib 2.0";
... to anything you like.
In the beginning, MailMergeSender is much like System.Net.Mail.SmtpClient: Create instance of the class and provide some SMTP related settings.
Setup the mail sender:
MailMergeSender mailSender = new MailMergeSender();
mailSender.MessageOutput = MessageOutput.SmtpServer;
Set up SMTP server login details:
mailSender.SmtpHost = "smtp.server.com";
mailSender.SmtpPort = 25;
mailSender.SetSmtpAuthentification("username", "password");
mailSender.LocalHostName = "my.localhostname.com"; // used in SMTP Hello command
Now here come some nice features that SmtpClient does not have. Add custom event handlers for OnBeforeSend, OnAfterSend, OnSendFailure, OnMergeBegin, OnMergeComplete, and OnMergeProgress:
mailSender.OnAfterSend += new EventHandler<MailSenderAfterSendEventArgs>(
delegate(object obj, MailSenderAfterSendEventArgs args)
{
// do something useful here, like updating a progress bar
});
For the send job, there are two alternatives:
mailSender.SendAsync(mmm, myDataTable);
DataTable, supply the row as variables to the MailMergeMessage and then call Send for each row. foreach (DataRow dr in myDataTable.Rows)
{
mmm.Variables = dr;
mailSender.Send(mmm);
}
Asynchronous send operations can be cancelled at any time:
// cancel the pending mail merge immediately
mailSender.SendCancel();
Timeout in milliseconds:
mailSender.Timeout = 100000;
Maximum number of failures until sending a message will finally fail:
mailSender.MaxFailures = 3;
Retry delay time between failures:
mailSender.RetryDelayTime = 3000;
Delay time between each message:
mailSender.DelayBetweenMessages = 1000;
By fixing the bugs in System.Net.Mail, I learned a lot about its design, but also at least as much about System.Reflection. Although all the bugs mentioned were reported to Microsoft a very long time ago, they didn't remove them. Life could be so easy, I'd even pay them for using my code... Anyway, MailMergeLib works well (again, after .NET 2.0 SP1 broke it).
System.Net.Mail and to find out ways to fix its bugs QPEncoder in DotNetOpenMail AttachmentBase.MimeTypes GetEncodedMailAddress fixed BugFixer class that .NET 2.0 SP1 / .NET 3.5 broke:
CorrectSubjectEncodedWordRFC2047compliant AddToAddressWithCorrectEncoding ClearMessageHeaders Tools class
CalcMailSize(MailMessage msg) now gives accurate size instead of an estimation GetMailAsMimeString (MailMessage msg) to retrieve the message content (same as in an EML file) Tools.WrapLine(string input, int length)will now allow empty lines (thanks to woetertie) QPEncoder from DotNetOpenMail might return a string 1 byte longer than RFC2027-compliant, fixed
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Sep 2008 Editor: Sean Ewington |
Copyright 2007 by Norbert Bietsch Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |