zoukankan      html  css  js  c++  java
  • 实例上下文模式:会话模式

    一、会话模式简介与示例代码  

    会话模式下,客户端和服务实例上下文、服务实例是一一对应关系,每一个客户端都在服务端都有自己对应的服务实例上下文。如下图所示
                     

      服务端使用会话模式的条件:

      1.使用支持会话模式的绑定,如WSHttpBinding、WS2007HttpBinding、NetTcpBinding、NetNamedPipeBinding。

     不支持会话的绑定有 BasicHttpBinding、NetMsmqBinding。Msmq属于离线状态的绑定,交互使用消息机制,因此不支持会话模式。

     2.上下文模式设置为PerSession。

     3.服务契约的SessionModel设置为Allowed或Required。

    如下代码设置:

       [ServiceContract(SessionMode=SessionMode.Allowed)]
        public interface ICalculator
        {
            [OperationContract]
            int Add();
        }
        [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
        public class Calculator : ICalculator
        {
            public int i = 0;
            public int Add()
            {
                return ++i;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine(new WSHttpBinding);
                ServiceHost host = new ServiceHost(typeof(Calculator));
                host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://localhost:4216");
                host.Opened += delegate { Console.WriteLine("Service Start!"); };
                host.Open();
                Console.Read();
            }
        }
    

     创建两个客户端时,都调用Add方法。因为两个客户端都是拥有各自的服务实例上下文,所以i值不是共享的,

    下面是客户端代码,分别创建两个client1和client2,它们用于自己的i值。

     static void Main(string[] args)
            {
               ICalculator client1= ChannelFactory<ICalculator>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:4216"));
               Console.WriteLine("client1:" + client1.Add());
               ICalculator client2 = ChannelFactory<ICalculator>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:4216"));
               Console.WriteLine("client2:" + client2.Add());
               Console.WriteLine("        " + client2.Add());
            }
    

     

    二、单例模式、单调模式、会话模式

          单调模式和单调模式唯一决定与InstanceContextMode的设置,与SessionMode无关。只要设置ServiceBehavior的InstanceContextMode设置为InstanceContextMode.PerCall或 Single已经决定了服务实例上下文的模式,与ServiceContract中的SessionMode无关。

         会话模式要求

          1.上下文模式设置为PerSession。

          2.SessionMode设置为Required或者Allowed。

          3.使用WSHttpBinding或WS2007HttpBinding时,保证开启安全模式和可靠性中的一个。若绑定既不可靠也不安全则无法实现会话模式。

          4.NetTcpBinding或者NetNamedPipeBinding默认使用会话信道,默认就是会话模式。

        WSHttpBinding和WS2007HttpBinding默认的安全模式为Message,不可靠会话。因此使用者两种绑定默认就是会话模式。

      static void Main(string[] args)
            {
                var binds = new List<WSHttpBinding>
                {
                    new WSHttpBinding(),
                    new WS2007HttpBinding()
                };
                binds.ForEach(b=>Console.WriteLine("Security:{0}  ReliableSession.Enabled:{1}",b.Security.Mode,b.ReliableSession.Enabled));
             }
    

     

    三、自定义实现会话模式

         会话模式的机制就是客户端的会话信道和服务实例上下文一一对应,那我们实现一个IInstanceContextProvider在内部维护会话信道和服务上下文实例的关系即可。下面代码所示,内部有一个Dictionary<IContextChannel, InstanceContext>,在InitializeInstanceContext函数调用时将会话信道和上下文存入字典,并用反射调用instanceContext的BindIncomingChannel方法,此方法将会话信道加入到上下文的内部信道管理器中,这样就不会每次处理完流程后都调用IsIdle来判断服务上下文是否空闲了。因为只有内部信道管理器中没有信道时才会调用IsIdle。

        GetExistingInstanceContext返回信道对应的上下文,并且将不处于打开状态的信道移除,这些信道就处于没有根引用状态,不久之后就会被GC回收。

     public class PersionProvider : IInstanceContextProvider
        {
            private Dictionary<IContextChannel, InstanceContext> dic = new Dictionary<IContextChannel, InstanceContext>();
            public InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel)
            {
                if (null == channel.InputSession)
                {
                    return null;
                }
                var closeChannels = (from c in dic.Keys where c.State != CommunicationState.Opened select c);
                if (null != closeChannels && closeChannels.ToList().Count > 0)
                {
                    closeChannels.ToList().ForEach(c => dic.Remove(c));
                }
                return dic.ContainsKey(channel) ? dic[channel] : null;
            }
    
            public void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel)
            {
                dic[channel] = instanceContext;
                MethodInfo info = typeof(InstanceContext).GetMethod("BindIncomingChannel", BindingFlags.NonPublic | BindingFlags.Instance);
                info.Invoke(instanceContext, new object[] { channel });
            }
    
            public bool IsIdle(InstanceContext instanceContext)
            {
                return true;
            }
    
            public void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext)
            {}
        }
    

      之后只需要在每个终结点分发器的运行时上设置我们自定义的PersionProvider即可。

        public class myPersionAttribute : Attribute, IServiceBehavior
        {
          public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
            {}
      public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {}
         public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers) { foreach (EndpointDispatcher endDiapacher in dispatcher.Endpoints) { endDiapacher.DispatchRuntime.InstanceContextProvider = new PersionProvider(); } } } }

     最后直接使用自定义的myPersionAttribute可以实现同样的会话模式效果。

        [myPersion]
        public class Calculator : ICalculator
        {
            public int i = 0;
            public int Add()
            {
                return ++i;
            }
        }
    
  • 相关阅读:
    漂亮的自适应宽度的多色彩CSS图片按钮
    Qt中设置widget背景颜色/图片的注意事项(使用样式表 setStyleSheet())
    QT的父子Widget之间消息的传递(如果子类没有accept或ignore该事件,则该事件会被传递给其父亲——Qlabel与QPushButton的处理就不一样)
    QT内置的ICON资源
    QT事件过滤器(QT事件处理的5个层次:自己覆盖或过滤,父窗口过滤,Application过滤与通知)
    QMetaObject感觉跟Delphi的类之类有一拼,好好学一下
    POJ 1013 小水题 暴力模拟
    WMDestroy函数调用inherited,难道是为了调用子类覆盖函数?还有这样调用的?
    技术资深、还关注市场的几率较高
    有感,懂市场比懂产品重要,懂产品比懂技术重要——想起凡客诚品和YY语音了
  • 原文地址:https://www.cnblogs.com/lh218/p/4539457.html
Copyright © 2011-2022 走看看