专栏首页GreenLeavesWCF系列教程之WCF中的会话

WCF系列教程之WCF中的会话

本文参考自http://www.cnblogs.com/wangweimutou/p/4516224.html,纯属读书笔记,加深记忆

一、WCF会话简介

1、在WCF应用程序中,回话将一组消息相互关联,从而形成一个回话(回话可以理解为一段时间内的通话,有开始,有结束),会话是服务端和客户端的终结点在在开始回话和结束回话这段时间内的所有消息的一个集合。

2、WCF中的回话机制通过设置服务协定ServiceContract上的SessionMode的枚举值来设置服务协定是否要求、允许或者拒绝基于回话的绑定.枚举值有以下三种:

(1)、Allowed:允许回话,这是SessionMode的默认值,当前协定允许使用会话,则客户端可以进行连接,并选择建立回话或者不建立回话,但是如果回话结束,然后还在当前回话通道发送消息,将会引起异常.

(2)、Required:要求回话,即所有调用(支持调用的基础消息交换)都必须是同一个会话的一部分,如果回话结束,然后还在当前回话通道发送消息,则会重新开一个通道,进行和服务端的通话

(3)、NotAllowed:禁止会话,即服务端不会与客户端进行消息交换。

3、影响WCF会话机制的因素

(1)、设置了SessionMode的值为Required,当采用的BasicHttpBinding时,因为BasicHttpBinding不支持会话,所以程序报错.

(2)、对于WSHttpBinding和WS2007HttpBinding,如果我们将安全模式设置为None(关闭安全会话)并且关闭可靠会话,他们也无法提供会话支持

(3)、对于NetTcpBinding和NetNamedPipeBinding来说,由于其传输类型本身具有支持会话的特性,所以采用了这两种绑定类型的终结点服务协定的会话模式不能设置为NotAllowed,即使关闭了安全会话和可靠会话也不行。

二、WCF中的回话和Asp.Net中的回话

1、WCF中回话的主要功能有以下:

(1)、他们由调用程序显示启动或者关闭

(2)、会话期间传递的消息按照接收消息的顺序进行处理。

(3)、会话将一组消息相互关联,从而形成对话。该关联的含义是抽象的。例如,一个基于会话的通道可能会根据共享网络连接来关联消息,而另一个基于会话的通道可能会根据消息正文中的共享标记来关联消息。可以从会话派生的功能取决于关联的性质。

(4)、不存在与 WCF 会话相关联的常规数据存储区。

2、Asp.Net中的回话由System.Web.SessionState.HttpSessionState 类提供功能,它的主要功能如下:

(1)、Asp.Net的回话是由服务器启动的

(2)、Asp.Net的回话原本是无序的

(3)、ASP.NET 会话提供了一种跨请求的常规数据存储机制。

三、代码示例

工程结构如下图所示:

1、WCF服务层搭建:新建契约层、服务层、和WCF宿主,添加必须的引用(这里不会的参考本人前面的随笔),配置宿主,生成解决方案,打开Host.exe,开启服务。具体的代码如下:

ICalculate.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace IService
{
    [ServiceContract
        (
        SessionMode = SessionMode.Required
        )
    ]
    public interface ICalculate
    {
        [OperationContract
            (
            IsTerminating = true//客户端调用Add后会话通道就会关闭会话
            )
        ]
        int Add(int a, int b);

        [OperationContract]
        int Divide(int value1, int value2);
    }
}

IUserInfo.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace IService
{
    [ServiceContract]
    public interface IUserInfo
    {
        [OperationContract]
        User[] GetInfo(int? id);
    }

    [DataContract]
    public class User
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }
        [DataMember]
        public string Nationality { get; set; }  
    }
}

注:必须引入System.Runtime.Serialization命名空间,应为User类在被传输时必须是可序列化的,否则将无法传输

Calculate.cs

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;

namespace Service
{
    public class Calculate : ICalculate
    {
        public int Add(int a, int b)
        {
            return a + b;
        }


        public int Divide(int a, int b)
        {
            try
            {
                return a / b;
            }
            catch (DivideByZeroException) {
                throw new FaultException("除数不能为0");//FaultException需要引用System.ServiceModel命名空间
            }
        }
    }
}

UserInfo.cs

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Service
{
    public class UserInfo : IUserInfo
    {
        public User[] GetInfo(int? id)
        {
            List<User> Users = new List<User>();
            Users.Add(new User { ID = 1, Name = "张三", Age = 11, Nationality = "China" });
            Users.Add(new User { ID = 2, Name = "李四", Age = 12, Nationality = "English" });
            Users.Add(new User { ID = 3, Name = "王五", Age = 13, Nationality = "American" });

            if (id != null)
            {
                return Users.Where(x => x.ID == id).ToArray();
            }
            else
            {
                return Users.ToArray();
            }
        }
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(Calculate)))
            {
                host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
                host.Open();
                Console.Read();
            }
        }
    }
}

App.Config

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>

    <services>
      <service name="Service.Calculate" behaviorConfiguration="mexBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/Calculate/"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="wsHttpBinding" contract="IService.ICalculate" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="mexBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

ok,打开Host.exe

服务开启成功!

2、创建一个名为Client的客户端控制台应用程序

Program.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Client.ServiceReference1.CalculateClient client = new Client.ServiceReference1.CalculateClient();
                Console.WriteLine("1+2={0}", client.Add(1, 2));
                Console.WriteLine("1+2={0}", client.Add(1, 2));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

通过给ICalculate的Add方法加上了IsTerminating=true,所以当客户端调用了一次Add方法之后,其与服务端的会话通道就会被关闭,所以第二次调用就会报错。

注意:因为默认的服务实例化模型(InstanceContextMode)采用PerSession,即每个服务实例都各自创建了一个会话通道,当Client调用Add后会话关闭,但Client1的会话通道并没有关闭,所以还是可以调用Add。但是如果将InstanceContextMode设置为单例模式,当一个客户端调用完Add方法之后,那么这个通道就被关闭了,另外一个客户端也无法调用了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • WCF系列教程之WCF客户端异常处理

    本文参考自:http://www.cnblogs.com/wangweimutou/p/4414393.html,纯属读书笔记,加深记忆 一、简介 当我们打开W...

    郑小超.
  • WCF系列教程之客户端异步调用服务

    本文参考自http://www.cnblogs.com/wangweimutou/p/4409227.html,纯属读书笔记,加深记忆 一、简介 在前面的随笔中...

    郑小超.
  • 控制反转和依赖注入模式

    一、控制反转和依赖注入两者搭配能像反射工厂那样解决程序集之间的耦合问题,下面将从Asp.Net经典的三层模式多方位的讲解控制反转和依赖注入模式,是如何帮我们进行...

    郑小超.
  • C#应用编程小例子-02-窗体最大化和最小化窗体大小

    landv
  • 数据访问模式之Repository模式

    数据访问层无非就是对数据进行增删改查,其中增、删、改等我们可以抽象出来写一个公共的接口或抽象类来定义这些方法,并采用一个基类实现这些方法,这样该基类派生的子类都...

    写代码的猿
  • 1066 图像过滤 (15 分)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    韩旭051
  • C#线程安全使用(三)

    在讲CancellationTokenSource之前我决定先讲一下lock和Interlocked,如果能很好的理解这两个,再去理解CancellationT...

    Kiba518
  • c#之线程总结(一)

    在我们做项目的时候会经常用到线程,但线程也不是万能的,用线程需要注意的东西也很多,自己做了一下总结 这次总结主要说三个部分 1 线程之委托方法 2 给线程传参 ...

    lpxxn
  • 牛X!纽约餐厅用数据说话,应对差评

    大数据文摘
  • C#委托+回调详解

    今天写不完,明天会接着写的,,,, 学习C#有一段时间了,不过C#的委托+回调才这两天才会用,以前只是知道怎么用.前面的一篇文章,函数指针,其实是为这个做铺垫的...

    杨奉武

扫码关注云+社区

领取腾讯云代金券