我心中的核心组件(可插拔的AOP)~第五回 消息组件
之所以把发消息拿出来,完全是因为微软的orchard项目,在这个项目里,将公用的与领域无关的功能模块进行抽象,形成了一个个的组件,这些组件通过引用和注入的方式进行工作,感觉对于应用程序的扩展性上有很大的提高,消息组件的提出是因为它的不固定性,从小方面说,项目模块的发消息的方式可能是不同的,有过模块是email,有的是数据库,有的是短信;而从大的方面说,对于项目与项目来说,它们发消息的方式也可能不同,所以,把它抽象出来,就显得很必要了。
对于一个消息来说,它的行为很固定,即发消息,Send,而考虑到网络阻塞问题,我们也同样提供了异常消息的发送,接口规范如下:
/// <summary>
/// Message Interface
/// Author:Garrett
/// </summary>
public interface IMessageManager
{
/// <summary>
/// Sends a message to a channel using a content item as the recipient
/// </summary>
/// <param name="recipient">A content item to send the message to.</param>
/// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
/// <param name="service">The name of the channel to use, e.g. "email"</param>
/// <param name="properties">A set of specific properties for the channel.</param>
void Send(string recipient, MessageType type, string subject, string body); /// <summary>
/// Sends a message to a channel using a set of content items as the recipients
/// </summary>
/// <param name="recipients">A set of content items to send the message to. Only one message may be sent if the channel manages it.</param>
/// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
/// <param name="service">The name of the channel to use, e.g. "email"</param>
/// <param name="properties">A set of specific properties for the channel.</param>
void Send(IEnumerable<string> recipients, MessageType type, string subject, string body); /// <summary>
/// Async Sends a message to a channel using a set of content items as the recipients
/// </summary>
/// <param name="recipients">A set of content items to send the message to. Only one message may be sent if the channel manages it.</param>
/// <param name="type">A custom string specifying what type of message is sent. Used in even handlers to define the message.</param>
/// <param name="service">The name of the channel to use, e.g. "email"</param>
/// <param name="properties">A set of specific properties for the channel.</param>
/// <param name="isAsync">is Async</param>
void Send(IEnumerable<string> recipients, MessageType type, string subject, string body, bool isAsync); }
有了规范,就是实现这个规范,我们以Email为例,看一下 Email消息发送的实现代码:
/// <summary>
/// 默认发消息服务
/// </summary>
public class EmailMessageManager : IMessageManager
{ #region Delegate & Event
/// <summary>
/// 加入发送队列中
/// </summary>
/// <param name="context"></param>
public delegate void SendingEventHandler(MessageContext context);
/// <summary>
/// 发送消息
/// </summary>
/// <param name="context"></param>
public delegate void SentEventHandler(MessageContext context);
/// <summary>
/// 加入发送队列中
/// </summary>
public event SendingEventHandler Sending;
/// <summary>
/// 发送消息
/// </summary>
public event SentEventHandler Sent; void OnSending(MessageContext context)
{
if (Sending != null)
Sending(context);
}
void OnSent(MessageContext context)
{
if (Sent != null)
Sent(context);
}
#endregion #region IMessageManager 成员 public void Send(string recipient, MessageType type, string subject, string body)
{
Send(new List<string> { recipient }, type, subject, body);
} public void Send(IEnumerable<string> recipients, MessageType type, string subject, string body)
{
Send(recipients, type, subject, body, false);
} public void Send(IEnumerable<string> recipients, MessageType type, string subject, string body, bool isAsync)
{
if (recipients != null && recipients.Any())
{
using (SmtpClient client = new SmtpClient()
{
Host = MessageManager.Instance.Host,
Port = MessageManager.Instance.Port,
Credentials = new NetworkCredential(MessageManager.Instance.UserName, MessageManager.Instance.Password),
EnableSsl = false,//设置为true会出现"服务器不支持安全连接的错误"
DeliveryMethod = SmtpDeliveryMethod.Network,
})
{
#region Send Message
var mail = new MailMessage
{
From = new MailAddress(MessageManager.Instance.Address, MessageManager.Instance.DisplayName),
Subject = subject,
Body = body,
IsBodyHtml = true,
};
MailAddressCollection mailAddressCollection = new MailAddressCollection();
recipients.ToList().ForEach(i =>
{
mail.To.Add(i);
});
if (isAsync)
{
client.SendCompleted += new SendCompletedEventHandler(client_SendCompleted);
client.SendAsync(mail, recipients);
}
else
{
client.Send(mail);
}
#endregion
}
}
} void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
string arr = null;
(e.UserState as List<string>).ToList().ForEach(i => { arr += i; });
//发送完成后要做的事件,可能是写日志
} #endregion
}
OK,有了消息的行业,我们再来看它的主体,即消息体,一般由发送者,接受者集合,主题,正文,是否立即发送等几个参数组成,下面来分享一下:补充一句,如果消息体的实现是单一的,我们可以把行为写在消息体里,形成一个消息上下文,微软很多架构都是这样做的,如EF数据上下文DbContext,ObjectContext,linq to sql上下文DataContext,Http上下文HttpContext等等。
/// <summary>
/// 消息实体
/// </summary>
public class MessageContext
{
/// <summary>
/// 消息类型
/// </summary>
public MessageType Type { get; set; }
/// <summary>
/// 消息头
/// </summary>
public string Subject { get; set; }
/// <summary>
/// 消息正文
/// </summary>
public string Body { get; set; }
/// <summary>
/// 接受方地址列表
/// </summary>
public IEnumerable<string> Addresses { get; set; }
/// <summary>
/// 是否处于准备发送状态
/// </summary>
public bool MessagePrepared { get; set; } public MessageContext()
{
Addresses = Enumerable.Empty<string>();
}
}
有了主体,再来看一下主要消息的设置,我们可以使用config中的section节点来做这事,如下:
/// <summary>
/// Message块,在web.config中提供Message块定义
/// </summary>
internal class MessageSection : ConfigurationSection
{
/// <summary>
/// 账号
/// </summary>
[ConfigurationProperty("UserName", DefaultValue = "bfyxzls")]
public string UserName
{
get { return (string)this["UserName"]; }
set { this["UserName"] = value; }
}
/// <summary>
/// 密码
/// </summary>
[ConfigurationProperty("Password", DefaultValue = "gemini123")]
public string Password
{
get { return (string)this["Password"]; }
set { this["Password"] = value; }
}
/// <summary>
/// 邮件服务器地址
/// </summary>
[ConfigurationProperty("Host", DefaultValue = "smtp.sina.com")]
public string Host
{
get { return (string)this["Host"]; }
set { this["Host"] = value; }
}
/// <summary>
/// 端口号
/// </summary>
[ConfigurationProperty("Port", DefaultValue = "")]
public int Port
{
get { return (int)this["Port"]; }
set { this["Port"] = value; }
}
/// <summary>
/// 发件人的email地址
/// </summary>
[ConfigurationProperty("Address", DefaultValue = "bfyxzls@sina.com")]
public string Address
{
get { return (string)this["Address"]; }
set { this["Address"] = value; }
}
/// <summary>
/// 发送之后,在收件人端显示的名称
/// </summary>
[ConfigurationProperty("DisplayName", DefaultValue = "占占")]
public string DisplayName
{
get { return (string)this["DisplayName"]; }
set { this["DisplayName"] = value; }
} }
config中的配置信息如下:
<configuration>
<configSections>
<section name="MessageSection" type="Messaging.MessageSection"/>
</configSections>
<MessageSection UserName="" Password="gemini123" Host="smtp.qq.com" Port="" Address="853066980@qq.com" DisplayName="大占"></MessageSection>
</configuration>
OK,我们再来看一下,消息的类型,目前可以定义两种消息,当然,为了组件的扩展性,你的类型可以通过IoC去代替它,这样可以避免程序中的硬编码,维护更方便
/// <summary>
/// 消息类型
/// </summary>
public enum MessageType
{
/// <summary>
/// 电子邮件
/// </summary>
Email,
/// <summary>
/// 短信息
/// </summary>
ShortMessage, }
最后来看一下生产消息的对象,使用了简单工厂模式,当然这只是个例子,实现中,它应该是使用IoC动态生产的,呵呵。
/// <summary>
/// 消息生产者
/// </summary>
public class MessageFactory
{
/// <summary>
/// 消息对象
/// </summary>
public static IMessageManager Instance
{
get
{
MessageType messageType = MessageType.Email;//通过配置产生
switch (messageType)
{
case MessageType.Email:
return new EmailMessageManager();
case MessageType.ShortMessage:
throw new NotImplementedException("没实现呢,呵呵");
default:
throw new ArgumentException("您输入的参数有误");
}
}
}
}
我心中的核心组件(可插拔的AOP)~第五回 消息组件的更多相关文章
- 我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器
回到目录 AOP面向切面的编程,也称面向方面的编程,我更青睐于前面的叫法,将一个大系统切成多个独立的部分,而这个独立的部分又可以方便的插拔在其它领域的系统之中,这种编程的方式我们叫它面向切面,而这些独 ...
- 我心中的核心组件(可插拔的AOP)~大话开篇及目录
回到占占推荐博客索引 核心组件 我心中的核心组件,核心组件就是我认为在项目中比较常用的功能,如日志,异常处理,消息,邮件,队列服务,调度,缓存,持久化,分布式文件存储,NoSQL存储,IoC容器,方法 ...
- 我心中的核心组件(可插拔的AOP)~第四回 异常拦截器
回到目录 之前说过有关拦截器的文章,第二回 缓存拦截器,事实上,在那讲里说的最多是AOP和缓存组件,对于拦截的概念并没有详细的说明,这一讲,不说AOP,主要说一下拦截器,拦截器Interceptio ...
- 我心中的核心组件(可插拔的AOP)~第六回 消息组件~续
回到目录 上一回写消息组件已经是很久之前的事了,这一次准备把消息组件后续的东西说一下,事实上,第一篇文章主要讲的是发消息,而这一讲最要讲的是收消息,简单的说,就是消息到了服务器之后,如何从服务器实时的 ...
- 我心中的核心组件(可插拔的AOP)~调度组件quartz.net
回到目录 quartz.net是一个任务调度组件,它可以灵活的设置你的调试方式,按时间,按日期,按周期都可以很容易的实现,quartz不仅可以用在web中,而且还可以部署在winform,winser ...
- 我心中的核心组件(可插拔的AOP)~第十二回 IoC组件Unity
回到目录 说在前 Ioc组件有很多,之前也介绍过autofac,castle等,今天再来说一下在微软Nlayer DDD架构里使用的unity组件,今天主要说一下依靠注入,如果希望看拦截的用法,可以阅 ...
- 我心中的核心组件(可插拔的AOP)~第十三回 实现AOP的拦截组件Unity.Interception
回到目录 说在前 本节主要说一下Unity家族里的拦截组件,对于方法拦截有很多组件提供,基本上每个Ioc组件都有对它的实现,如autofac,它主要用在orchard项目里,而castle也有以拦截的 ...
- 我心中的核心组件(可插拔的AOP)~第十五回 我的日志组件Logger.Core(策略,模版方法,工厂,单例等模式的使用)
回到目录 之前的讲过两篇关于日志组件的文章,分别是<第一回 日志记录组件之自主的Vlog>和<第三回 日志记录组件之log4net>,而今天主要说一下我自己开发的另一种日志 ...
- 我心中的核心组件(可插拔的AOP)~调度组件quartz.net续~任务管理器的开发
回到目录 对于任务调度来说,越来越多的团队选择了quartz,它在java和.net环境下表现都十分优秀,配置简单,功能强大,时间表达式配置灵活,但在使用时,还是感觉缺点什么,怎么说,你在服务器上安装 ...
随机推荐
- VIM的一些操作小技巧
vim的设计理念是:组合. 命令的组合,模式的组合, 普通模式 左: h 上:k 下:j 右 : l i : 当前光标处插入 I: 到光标所在行的行首进入插入模式 a: 在当前光标的后一位 ...
- 【转】Linux 文件夹文件创建与删除
[转自:Linux文件夹文件创建.删除 - 风生水起 - 博客园] 1. 删除文件夹 rm -rf fileNamede> -删除文件夹实例:rm -rf /var/log/httpd/acc ...
- poi解析excel 03、07
maven依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</a ...
- Android中的requestWindowFeature
朋友推荐使用博客记录工作中的难点以及常犯的错误,仅作为笔记,首先整理之前的工作日志. requestWindowFeature(featrueId),它的功能是启用窗体的扩展特性: 注意:该方法必须放 ...
- JavaScript 基础第九天(DOM第三天)
一.引言 我们昨天介绍了很多的概念以及大部分我们可以在工作中用到的事件,那么今天我们将运用这些知识做一些效果! 二.导入 今天的内容以实例为主. 三.重点内容 祝愿墙的简单构建: 首先我将介绍出本次实 ...
- 解析html并使用canvas进行渲染
在学习html5的时候,使用canvas实现了对html文本的解析和渲染,支持的tag有<p>.<i>.<b>.<u>.<ul>.<l ...
- Failed to load JavaHL Library.
以前使用的电脑是32位的,安装的svn可以正常使用,但是现在的电脑室64位的,安装好svn后,把项目提交到svn的过程中,总是弹出来一个错误的对话框: Failed to load JavaHL Li ...
- 工作当中实际运用(3)——js原生实现鼠标点击弹出div层 在点击隐藏
function onmou(){ var divs=document.getElementById('kefuDV');//获取到你要操作的div if (divs.style.display==& ...
- 【转】js写显示农历的日期
网上查找了个,记录下. <body> <!-- 中国农历开始 --> <SCRIPT language=JavaScript> <!-- var lunarI ...
- SQLServer 获取第几周开始日期
不多说直接上code DECLARE @CurrDay DATETIME=GETDATE() --SET @CurrDay=CAST(('2013-01-10')AS DATETIME) --SET ...