我心中的核心组件(可插拔的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 = "25")]
        public int Port
        {
            get { return (int)this["Port"]; }
            set { this["Port"] = value; }
        }
        /// <summary>
        /// 发件人的email地址
        /// </summary>
        [ConfigurationProperty("Address", DefaultValue = "[email protected]")]
        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="853066980" Password="gemini123" Host="smtp.qq.com" Port="25" Address="[email protected]" 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)