电子邮件通知框架






4.05/5 (9投票s)
一个 .NET 受管框架,用于发送 HTML 或纯文本的模板电子邮件
引言
我在行业里工作了一段时间,我了解到没有哪个应用程序不需要发送电子邮件。现在,在创建免费 SMTP 服务器方面已经做了很多工作,并且 .NET 中有丰富的 SMTP 支持 API 集,但我还没有找到一个可以让我发送电子邮件的简单框架。
那么,我希望这样的框架具备哪些能力呢?我希望它能够发送简单的文本和 HTML 电子邮件,能够通过一些参数自定义电子邮件内容,能够配置电子邮件认证(匿名、基本或集成)。此外,系统自动发送的大部分电子邮件消息都带有模板,其中一些键会在运行时被替换,拥有提供这些功能的框架也是我所期望的。
本框架就是围绕这些设计目标而设计的,我相信它可以开箱即用,并且可以在各种扩展点进行扩展。该框架可以作为 WCF 组件进行部署。
本文布局
本文主要分为四个部分
-
架构 - 本节内容几乎不能称为架构,因为它过于详细,但它将提供一个很好的流程。
-
设计 - 处理应用程序的设计、方法的职责等。
-
代码 - 在本节中,将讨论附带解决方案中一些更有趣的代码片段。
-
如何操作 - 在本节中,将描述配置和发送电子邮件所需的步骤。如果您想将其作为开箱即用的实用工具,可以跳过架构、设计和代码部分。
架构

上图显示了通知子系统的架构。显然,这个邮件系统是一个客户端代理,邮件服务器是一个外部组件。
通知系统通过部署为进程内 DLL 或 WCF 服务,允许进行进程内和进程外调用。
通知系统块包含一个消息创建器(也公开为 WCF 服务)、一组消息 API、消息类型的枚举以及消息模板数据存储。本节详细介绍了流程以及每个组件的职责。
WCF 接口 - 此接口/实现的类提供了可以由组件调用的业务特定方法。
消息创建器 - 这是系统的核心,外部系统(WCF 客户端或本地进程)调用它来创建和发送消息。它负责从消息模板数据存储中加载消息模板,用实际值(例如,收件人的姓名)替换键,并调用消息 API。
通知类型 - 这是业务方法和通知模板之间的强类型映射。
消息模板 - 这是一个数据存储,其中包含原始消息模板。
消息 API - 这是 .NET 通知 API。
客户端 - 此系统的客户端可以是 WCF 客户端,也可以是将应用程序作为 DLL 引用的进程内应用程序。
邮件服务器 - 这是一个 SMTP 服务器,将发送电子邮件。
设计
本节详细介绍了框架的设计。我们主要从对象模型开始,将讨论保持在高层,并关注对逻辑设计重要的点。例如,我们不会纠结于配置创建如何工作、我们将使用哪种 WCF 通信协议或通知 API 将如何使用等细节。
类模型

本节详细介绍了用于实现上述通知框架架构的类的职责。
BusinessNotificationAdapter
- 此类包含业务使用的方法。它也作为服务在 WCF 端公开。其职责是拦截传入的业务请求,识别它们的枚举,并调用通知控制器。AbstractNotificationController
-此类是主要的协调者。它获取电子邮件模板(正文和主题),在不了解传输机制或身份验证机制的情况下准备电子邮件消息,并调用EmailNotificationProvider
。ResxNotificationController
-此类是AbstractNotificationController
的特定实现,它使用一对 RESX 文件作为模板存储。EmailNotificationProvider
-此类负责使用服务器实际通知消息。它查找通知机制的配置,例如 SMTP 服务器地址、SMTP 端口、电子邮件优先级等。
序列图

上图显示了发送消息的典型控制流。业务对象请求发送通知。业务通知适配器找出通知类型的 Enum
,并调用通知控制器上的 Notify
。配置定义了将使用哪个具体类进行通知。notify
方法准备消息,并请求 Email notification provider(本质上是通知 API 的包装器)发送电子邮件消息。
代码
在本节中,我们将讨论一些更重要的代码片段。代码是使用 Visual Studio 2010 开发的,但应该可以在 VS 2008 中编译。我们不会讨论所有代码,但您会发现附带的代码已完全文档化。
BusinessNotificationAdapter
此类包含业务特定的方法。该方法创建占位符的泛型列表,并找到与要发送的电子邮件对应的 enum
。它准备泛型方法并分派调用。此类主要目的是屏蔽业务免受电子邮件消息准备细节的影响,并提供简单、结构化且类型安全的发送电子邮件的方法。
public void SendSampleNotification(string from, string subjectPlaceholde1,
string subjectPlaceholder2, string bodyPlaceholder1, string bodyPlaceholder2,
IList<string> toList) {
List<KeyValuePair<string, string>> placeholdersDataForBody =
new List<KeyValuePair<string, string>>();
placeholdersDataForBody.Add(new KeyValuePair<string,
string>("@Placeholder1", bodyPlaceholder1));
placeholdersDataForBody.Add(new KeyValuePair<string,
string>("@Placeholder2", bodyPlaceholder2));
List<KeyValuePair<string, string>> placeholdersDataForSubject =
new List<KeyValuePair<string, string>>();
placeholdersDataForSubject.Add(new KeyValuePair<string,
string>("@Placeholder1", subjectPlaceholde1));
placeholdersDataForSubject.Add(new KeyValuePair<string, string>
("@Placeholder2", subjectPlaceholder2));
//hard coding for now, we can use factory if desired
AbstractNotificationController notificationController =
new ResxNotificationController.ResxNotificationController();
notificationController.PrepareAndSendEmail
(NotificaionType.SampleNotification, placeholdersDataForBody,
placeholdersDataForSubject, toList, null, null, from, null);
}
AbstractNotificationController
此类主要包含准备和发送电子邮件的方法。该类需要具体的实现来查找主题、正文方法以及替换占位符数据的机制。
public void PrepareAndSendEmail(NotificaionType typeOfNotification,
IList<KeyValuePair<string, string>> placeholdersDataForBody,
IList<KeyValuePair<string, string>> placeholdersDataForSubject,
IList<string> sendToAddress, IList<string> ccList, IList<string> bccList,
string sendFromAddress, IList<string> attachmentList)
{
EmailNotificationProvider emailNotificationProvider = new EmailNotificationProvider();
string mailBody = ReplaceBodyPlaceholders
(GetBodyTemplate(typeOfNotification), placeholdersDataForBody);
string subject = ReplaceSubjectPlaceholders
(GetSubjectTemplate(typeOfNotification), placeholdersDataForSubject);
//Fix this method to get encoding and priority from config
emailNotificationProvider.SendNotification
(sendFromAddress, sendToAddress, ccList, bccList, mailBody, subject, attachmentList);
ResxNotificationController
此类负责精确实现查找模板和替换占位符数据。当前实现使用资源文件,但也可以使用数据库或平面文件模板。
protected override string GetSubjectTemplate(NotificaionType typeOfNotification)
{
switch (typeOfNotification)
{
case NotificaionType.SampleNotification:
return EmailSubjectDataStore.SampleNotification;
break;
}
throw new ArgumentException();
}
EmailNotificationProvider
此类使用 .NET SMTP 客户端 API 发送消息。其主要职责是双重的;管理配置,例如如何获取 SMTP 服务器地址等,并为框架提供 SMTP 客户端的包装器。
如何操作
本节详细介绍了如何从配置和开发新电子邮件模板的角度使用此框架
-
配置
-
打开宿主程序集的 App.Config/Web.Config
-
添加以下键(请参阅
NotificationTestingUtility
项目的示例 app.config)键 描述 可能的值 MailFormat
这是将要发送的电子邮件的格式 HTML
文本
MailPriority
电子邮件消息的优先级 低功耗
正常
高
MailEncoding
这是编码格式
0
SmtpServer
SMTP 服务器的 IP 地址或名称
SmtpPort
服务器正在监听的端口。如果未指定,则默认为 25
AuthenticationType
SMTP 服务器的身份验证类型。如果类型为 Basic,则必须指定 SMTP 用户名、域和密码
Basic
匿名
IntegratedAuthentication
SMTPUserName
用户名称
SMTPPassword
用户密码
SMTPDOmain
用户的域
-
-
开发新模板
-
使用唯一的字符(如 % 或 @)准备带有占位符的主题和正文模板。
-
将主题和正文添加到
EmailBodyDataStore
和EmailSubjectDataStore
资源文件中。 -
为新添加的模板在
NotificationTypeEnum
中添加一个新的enum
值。 -
更新
ResxNotificationController
方法,为新的通知类型返回正文和主题。 -
添加一个新的业务方法,该方法接受模板中占位符的值。
-
通过一些技巧,您甚至可以填充值列表。如果您在这方面遇到困难,请告诉我。
历史
- 2009 年 10 月 5 日:初稿