MailMergeLib - A .NET Mail Client Library

原文地址:http://www.codeproject.com/KB/IP/MailMergeLib.aspx

Introduction

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.

简介

在我使用 .NET 1.1 进行工作的期间,我开始使用Mike Bridge的邮件客户端 DotNetOpenMail真的做了很好的工作。基于这个,我在一个Web service里建立了一个基本的邮件合并库。

 

在进入.NET 2.0后,我认为那个 DotNetOpenMail 已经成为了一个陈旧的东西,因为微软已经引入了他们的 System.Net.Mail库。今天我仍然认为那(System.Net.Mail)是很好的设计代码,但是它有一些讨厌的Bug并且有违于RFC规则 接连地被我发现出来,并且它至今不能被解决。所以这个邮件合并库渐渐改进并且变得使用相当舒适。

 

Background

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:

  1. 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
  2. Display names of mail addresses that are not all 7bit characters must be encoded. This works fine with every address type except for 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.
  3. 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.
  4. With 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.
  5. Attachments to a mail message must not have white space in the file name, which is neglected by System.Net.Mail.
  6. Setting the transfer encoding to 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
  7. Quoted-Printable encoding is not limited to a maximum of 76 characters in 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?

背景

System.Net.Mail,我没有遇到任何的阻碍,它将发送信息。

 

要点是:对于你的信息,一些BugRFC违规将会增加垃圾邮件过滤器的垃圾邮件等级,并且一些邮件客户端可能会显示出垃圾的部分。

 

当在System.Net.Mail上工作的时候,我发现和修改了以下的bug

 

1.       MailMessage.MailAddressCollection.Clear方法没有清空相应的Header。如果你希望对不同的收件人重新使用一个现有的MailMessage对象,你不得不自己移除这个收件人的Header(e.g. MailMessage.Headers.Remove("to"))-.Net 2.0 SP1里被改正。

2.       邮件地址的显示名称一定没有被完全地编码成 7bit。这对除了 To 地址外的每个地址起了好的作用。To 地址将永远不会被编码,不论你对一个新的邮件地址使用了哪一种的编码参数。- .Net 2.0SP1里被修正,只可惜空隔仍然没有被编码。

3.       MailMessae.To.ToString()方法只有在信息被发送出去后才会返回编码后的字符串,依照文档,这应该成为一个案例无论这个信息是否已经被发送与否。

4.       关于MailMessage.Headers,那儿有一个bug,在那里,在一个编码后的文本里会有一段白色的空隔,这将会导致 non-RFC 2047顺从信息,那将会增加了垃圾邮件等级。

5.       邮件附件的文件名里,一定不能有空隔,但这个被System.Net.Mail忽视了。

6.       设置一个文件编码为TransferEncoding.SevenBit,却变成了一个Header 文本sevenbit,而不是7bitSevenbit不是一个RFC依据并且会在一些邮件客户端里惹起问题。- .Net 2.0 SP1里被修正。

7.       System.Net.Mail里,Quoted-Printable 编码没有被限制于76字符的最大值。RFC2045里需要Quoted-Printable 编码行不超过 76字符长。如果长的行被使用Quoted-Printable 编码来编码,”Soft” lines被截断。(这里soft lines不知道怎么译)

 

尽管微软已经提供了一些bug的修复,但相当多的bug.net 3.5 sp1(包括了.net 2.0 sp1)里还是没有被修正。所以我尽力去找一些方法来自主修正它们。所有的bug修正都被包括在一个静态类 Bugfixer 里,它将有望不需要今后的.net版本。微软的天空大饼?

 

Using the Code

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.

使用代码

为了在 System.Net.Mail里发送一个电子邮件,你将首先创建一个MailMessage,然后使用SmtpClient来发送它。在MailMergeLib里这相当的类似:你将创建一个MailMergeMessage,然后使用MailMergeSender发送它。

 

MailMergeMessage

Create a New Message

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.

创建一个新的Message

MailMergeLib的一大优势是占位符。{Placeholders}是一个嵌入到任意文本上的一个DataTable字段名称。

那么首先创建一个信息并且调整一些设置。CultureInfo 是相应的为格式化包含日期,currency或者数字数据的占位符。

// 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;

 

Formatting Capabilities

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.

 

格式化容器

它可能去添加标准的.NET格式化属性到你的占位符。一个日期将会显示这个日期数量和这个月的名称,你将会写:{Date:”{0:dd MMMM}”}

 

万一DataRow的一列将有扩展属性nullformat,这些属性将会被使用。例如:

 

当然列类型和格式化属性必须要相互适合。

 

Message Body

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.

信息主体

这个信息主体部分能够被相当容易地添加,通过把它们在构造器里当作一个参数来使用,或者设置属性:

 

HtmlText PlainText 能够包含占位符。你可以通过使用特定的语法{IncludeFile:”File”}来插入一个文件文件。IncludeFile DataTable的一个列名,file意味着解释文件名内容。

 

Attachments

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

 

附件

 

你也可能希望通过文件名称占位符来添加一些个性化的附件。

 

并且那是添加字符串附件的方法:

 

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

 

邮件地址

 

为了发送一封电子邮件,我们至少需要提供一个收件人地址和一个发件地址。再次,利用占位符使它能够创建个性化的电子邮件。

 

Miscellaneous

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.

杂项

 

如果你的数据是从一个DataTable来的,你的收件人可能会是空的电子邮件字段。那你可能希望设置:

 

这将不会因为空地址而抛出一个异常。

 

希望改变MailMergeLib的标识?设置

 

任何你想要的。

 

MailMergeSender

In the beginning, MailMergeSender is much like System.Net.Mail.SmtpClient: Create instance of the class and provide some SMTP related settings.

SMTP 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

 

MailMergeSender

首先,MailMergeSender非常像System.Net.Mail.SmtpClient:创建一个类的实例和提供一些SMTP的相关设置。

 

SMTP设置

 

设置Mail sender:

 

设置SMTP服务器登录信息:

 

EventHandlers

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
         });

 

EventHandlers

 

现在这里来了一些SmtpClient所没有的很好的特色。为OnBeforeSend, OnAfterSend, OnSendFailure, OnMergeBegin, OnMergeComplete, OnMergeProgress添加自定义的Event handlers

 

Sending a Message

For the send job, there are two alternatives:

  1. Start to send messages as an asynchronous operation, which will not block the calling thread:
mailSender.SendAsync(mmm, myDataTable);
  1. Start to send messages as a synchronous operation. In this case, you will loop through the rows of your 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);
} 

 

发送一个信息

 

对于一个发送任务,这里有两种选择:

1.       当作一个异步操作来发送一个信息,这将不会被调用的线程所阻碍。

mailSender.SendAsync(mmm, myDataTable);

2.       使用一个同步操作来发送一个信息。既然这样,你将循环你的DataTable的行,提供行的变量给MailMergeMessage,并每行调用Send方法。

 

Cancelling a Send Operation

Asynchronous send operations can be cancelled at any time:

// cancel the pending mail merge immediately
mailSender.SendCancel();

 

取消一个发送操作

 

异步发送操作能够被随时取消:

// cancel the pending mail merge immediately
mailSender.SendCancel();

 

Influencing Error Handling

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;

 

影响的错误处理

 

毫秒级的超时设置:

mailSender.Timeout = 100000;

发送失败的最大失败次数:

mailSender.MaxFailures = 3;

失败重试的间隔时间:

mailSender.RetryDelayTime = 3000;

每次发送信息的间隔时间:

mailSender.DelayBetweenMessages = 1000;

Conclusion

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里修正这些bug,我学到了很多关于它的设计,和同样多的System.Reflection的设计。尽管所有的bug在很久以前已经报告给了微软,但微软还没有移除它们。生命如此短暂,我却把它们付给了我的代码无论如何,MailMergeLib运行得非常好(再一次,在.NET 2.0 SP1 超过它后)。

 

Special Thanks To

  • .NET Reflector for helping to dive deeply into System.Net.Mail and to find out ways to fix its bugs
  • Mike Bridge for his QPEncoder in DotNetOpenMail
  • People working on Mono 1.2.3.1 for their AttachmentBase.MimeTypes
  • All authors of inspiring articles on The Code Project that I have learned from

特别感谢

.NET Reflector,因为有助于深入钻研System.Net.Mail和找出修正bug的方法。

Mike Bridge ,因为他在DotNetOpenMail里的QPEncoder

Mono 1.2.3项目工作者,因为他们的AttachmentBase.MimeTypes

CodeProject网站上的支持者。

History

  • 2007-07-10: Initial release
  • 2007-07-11: Minor doc and code update
  • 2007-10-08: SSL support added (thanks to WPKF), GetEncodedMailAddress fixed
  • 2007-12-28: Re-designed bug fixes in BugFixer class that .NET 2.0 SP1 / .NET 3.5 broke:
    • CorrectSubjectEncodedWordRFC2047compliant
    • AddToAddressWithCorrectEncoding
    • ClearMessageHeaders
  • 2008-01-01: Improvement of the Tools class
    • CalcMailSize(MailMessage msg) now gives accurate size instead of an estimation
    • Added GetMailAsMimeString (MailMessage msg) to retrieve the message content (same as in an EML file)
  • 2008-01-12: Minor improvements
    • 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
  • 2008-05-22: Minor improvement
    • Fixed potential problem with zip files
  • 2008-08-30: Minor improvements
    • Verified: Bugs still exist in .NET 3.5 SP1, and bugfixing of MailMergeLib works with it
    • Updated documentation

历史记录

  • 2007-07-10: 最初的发布版本
  • 2007-07-11: 小文档和代码更新
  • 2007-10-08: 添加了SSL支持(感谢 WPKF), GetEncodedMailAddress 修正
  • 2007-12-28: BugFixer 类里重新设计了bug 修正 that .NET 2.0 SP1 / .NET 3.5 broke:
    • CorrectSubjectEncodedWordRFC2047compliant
    • AddToAddressWithCorrectEncoding
    • ClearMessageHeaders
  • 2008-01-01: 改进了 Tools
    • CalcMailSize(MailMessage msg) 现在给了精确的大小来代替估计。
    • Added GetMailAsMimeString (MailMessage msg) to retrieve the message content (same as in an EML file)
  • 2008-01-12:  小改进
    • Tools.WrapLine(string input, int length)现在允许空行(感谢 woetertie)
    • QPEncoder from DotNetOpenMail might return a string 1 byte longer than RFC2027-compliant, fixed
  • 2008-05-22: 小改进
    • 修改了Zip文件的潜在问题。
  • 2008-08-30: 小改进
    • 核实: Bugs 仍然存在于 .NET 3.5 SP1, 并且MailMergeLib bugfixing 仍然对bug起作用。
    • 文档更新。

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

许可

这个文章,连同任何的相关资源和文件,在Code Project Open License(CPOL)下得到许可。

About the Author

Norbert Bietsch

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值