zoukankan      html  css  js  c++  java
  • 在asp.net中实现观察者模式,或有更好的办法?

      在asp.net中实现观察者模式?难道asp.net中的观察者模式有什么特别么?嗯,基于Http协议的Application难免有些健忘,我是这样实现的,不知道有没有更好的办法?

    先谈谈需求吧,以免陷入空谈

    最近一个Case, 这样的需求:很多客户端不断的向Web Application提交数据,管理员进入Web的管理页面可以即时的看到这些数据,有多个管理员可以同时浏览,且管理员浏览的数据从管理员开始监视那个时刻起,不能显示以前的数据。从这个场景一看,明显的观察者模式,管理员开始监视时,订阅数据,数据到达的时候向所有订阅了数据的管理员广播数据。

    需求如下图:

     

    在.net里面我们有事件(event),那就无需使用传统的观察者模式的模型了(如果不了解观察者模式请点击这里

    那么我首先实现一个Monitor类,这个类用来接收客户端传递来的数据并将数据广播出去

    public class DataEventArgs : EventArgs
    {
                
    public string Message
                
    {get;set;}
                
    public DataEventArgs(string message)
                
    {
                    
    this.Message = message;
                }

            }

    public class Monitor

            
    public event EventHandler<DataEventArgs> DataIn;
            
    private void SendData(string message)
            
    {
                
    if (DataIn != null)
                
    {
                    DataEventArgs e 
    = new DataEventArgs(message);
                    DataIn(
    this, e);
                }

            }

            
    /// <summary>
            
    /// 这个方法被一个HttpHandler调用,客户端向这个Handler发送数据
            
    /// 数据处理后作为字符串传递给该方法,该方法然后将数据广播出去
            
    /// </summary>
            
    /// <param name="message">处理后的数据</param>

            public void ReciveData(string message)
            
    {
                SendData(message);
            }

    }

     有了发布者还需要订阅者,我们实现管理员类,来订阅数据
    public class Admin
        
    {
            
    /// <summary>
            
    /// 用这个保存所有收到的数据
            
    /// </summary>

            public IList<string> MessageList
            
    getset; }
            
    public Admin(Monitor monitor)
            
    {
                MessageList 
    = new List<string>();
                monitor.DataIn 
    += new EventHandler< DataEventArgs>(ReciveMessage);
            }


            
    private void ReciveMessage(object sender, DataEventArgs e)
            
    {
                MessageList.Add(e.Message);
            }

        }


     

    Ok,需要具备的元素我们都写好了,但是如何让它们工作起来?如果使Winform程序,那将毫无悬念。

    分析:我们碰到的问题

    第一个问题:当客户端发送一个数据包,我们是实例化一个新的Monitor么?如果是,哪么每次实例化一个全新的Monitor,所有在它上面订阅的事件将全部消失了,如果不是那这个Monitor将如何存在呢?总不能真空吧,两个http请求之间如何保存数据呢?不过再把需求一读,好像整个应用程序中就只需要也只能有一个这样的Monitor呢,该是单件模式上场的时候了(关于单件模式请点击这里)

    在上面的Monitor的实现中添加下面的代码:

    private static Monitor _instance = null;
    public static Monitor Current
    {
        
    get 
       
    {
           
    if (_instance == null)
              _instance 
    = new Monitor();
          
    return _instance;
       }

    }

      但是本系统存在多个客户端,所以为了避免多线程造成问题,还是来Double Check一下吧,修改上面的代码如下:
    public static Monitor Current
            
    {
                
    get 
                
    {
                    
    object o = new object();
                    
    if (_instance == null)
                    
    {
                        
    lock (o)
                        
    {
                            
    if (_instance == null)
                                _instance 
    = new Monitor();
                        }

                    }

                    
    return _instance;
                }

            }


     

    (PS:为什么使用单件就可以跨请求保存实例了呢?因为这里使用了一个static member保存Monitor的引用,static member在.net的GC里面是被作为Root的,详细内容请参见框架程序设计那本书)

    第二个问题: 当管理员页面的ajax请求的时候,每两个请求如何保存数据?呵呵,上面那个问题不是说了么,用单件,但是单件是全局存在的,我们的管理员是多个,每个管理员可以决定是否订阅数据,以及什么时候订阅。想起来没?除了全局数据外我们还有Session

    在管理页面上我放置一个“开始监视”的按钮,这个按钮使用ajax请求服务器端的一个HttpHandler,在Handler的ProcessRequest方法里这样来做:

    Admin admin = context.Session["monitor_listener"as Admin;
    if(admin == null)
    {
         admin 
    = new Admin(Monitor.Current);
         context.Session[
    "monitor_listener"= admin;
    }


     

    注意,由于这个Handler需要访问Session,所以你需要让这个Handler继承IRequiresSessionState接口(为什么使用继承而不用实现这个术语?实际上这个接口是一个标记接口,没有任何需要实现的成员,只是标记这个Handler可以访问Session,我不知道为什么MS不使用Attribute,是不是更合理些)

    在管理页面还有个一个SetInterval不断的调用一个含有ajax的方法,去请求另外一个Handler,这个Handler将Admin收到的数据返回到web页面,让我们来看看这个Handler的部分实现:

    public void ProcessRequest(HttpContext context)
    {
          context.Response.Buffer 
    = true;
          context.Response.ExpiresAbsolute 
    = System.DateTime.Now.AddSeconds(-1);
         context.Response.Expires 
    = 0;
        context.Response.CacheControl 
    = "no-cache";
        Admin admin 
    = context.Session["monitor_listener"as Admin;
        
    if (admin == null || admin.MessageCollection == null || admin.MessageCollection.Count <= 0)
                    
    return;
       
    string[] messages = new string[admin.MessageCollection.Count];
       admin.MessageCollection.CopyTo(messages, 
    0);
       StringBuilder sb 
    = new StringBuilder();
       
    for (int i = 0; i < messages.Length; i++)
       
    {
            sb.AppendFormat(
    "<li>{0}</li>", messages[i]);
       }

       admin.MessageCollection.Clear();
       context.Session[
    "monitor_listener"= admin;
       context.Response.Write(sb);
       context.Response.Flush();
    }


     

    OK,一个在asp.net环境中实现的观察者模式基本上就算完成了,不过上面只有怎样订阅,那什么时候取消订阅了,可以在Session_End事件里面取消订阅

    还查看了一些关于长连接的文章,发现这个不错,准备改进一下。

    完整的代码稍后提供,希望这块转头能引来一些玉

  • 相关阅读:
    LDMIA、LDMIB、LDMDB、LDMDA、STMIA、LDMFD、LDMFA、LDMED、LDMEA指令详解
    汇编:MSR/MRS/BIC指令
    leetcode 题库解答
    70个Python练手项目
    idea+maven+Strtus2 之新建工程
    java中利用StringEscapeUtils对字符串进行各种转义与反转义
    springboot+springcache+shiro+Redis整合时@Cacheable、@Transactional等注解失效的问题
    springboot+springCache+Redis声明式缓存
    SpringBoot与Mybatis整合之Junit单元测试
    RabbitMQ在Windows中的安装
  • 原文地址:https://www.cnblogs.com/yuyijq/p/1179518.html
Copyright © 2011-2022 走看看