• 我心中的核心组件(可插拔的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 = "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="853066980" Password="gemini123" Host="smtp.qq.com" Port="25" 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("您输入的参数有误");
                    }
                }
            }
        }

    回到目录

  • 相关阅读:
    The Node.js Event Loop, Timers, and process.nextTick()
    Main event loop
    Why should I avoid blocking the Event Loop and the Worker Pool?
    HTML Standard系列:Event loop、requestIdleCallback 和 requestAnimationFrame
    在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
    多线程 主线程
    mvvm
    跨库事务
    nginx 死循环
    nginx proxy pass redirects ignore port
  • 原文地址:https://www.cnblogs.com/lori/p/3418264.html
Copyright © 2020-2023  润新知