WCF 会话状态和两个端点(EndPoint)之间的一系列消息交换相关联,它实际上是 "实例上下文(Instance Context)”,控制着服务对象实例的创建方式和生存期。和 ASP.NET Session 有很大不同。 WCF Session 特点:
1. 开启服务契约的 Session。 可以选择的模式包括:Required、Allowed、NotAllowed。Required 表示必须使用 Session,如果 Binding 不支持,则会抛出异常;Allowed 表示如果 Binding 支持 Session 则开启会话;NotAllowed 表示停用 Session。多数 Binding 缺省就会开始 Session,而 BaseHttpBinding 不支持 Session。 [ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculate { [OperationContract] int Add(int a, int b); } 2. 使用 ServiceBehaviorAttribute 和 InstanceContextMode 在服务契约的实现类型上指定服务对象的 "实例上下文模式"。 InstanceContextMode 可选择的方式包括:PerSession、PerCall、Single。PerSession 表示为每个连接(每个客户端代理对象) 创建一个会话(服务对象);PerCall 则为每次调用(Operate)创建一个会话(服务对象);Single 则表示所有的客户端共享一个会话(服务对象)。 [ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculate { [OperationContract] int Add(int a, int b); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] public class CalculateService : ICalculate { public CalculateService() { Console.WriteLine("Constructor {0}...", this.GetHashCode()); } public int Add(int a, int b) { Console.WriteLine(OperationContext.Current.SessionId); return a + b; } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(CalculateService)); host.AddServiceEndpoint(typeof(ICalculate), new WSHttpBinding(), "http://localhost:8080/calc"); host.Open(); }); ChannelFactory<ICalculate> factory = new ChannelFactory<ICalculate>(new WSHttpBinding(), "http://localhost:8080/calc"); ICalculate o = factory.CreateChannel(); Console.WriteLine(o.Add(1, 2)); Console.WriteLine(o.Add(1, 2)); Console.WriteLine(o.Add(1, 2)); } } 输出: Constructor 30136159... urn:uuid:9fc72020-3ba3-442b-be1c-f2faf06a46d3 3 urn:uuid:9fc72020-3ba3-442b-be1c-f2faf06a46d3 3 urn:uuid:9fc72020-3ba3-442b-be1c-f2faf06a46d3 3 3. 如果有必要,可以使用 OperationContractAttribute 的 IsInitiating 和 IsTerminating 属性来控制每次调用对 Session 的操控。 IsInitiating 表示该方法是否可以初始化 Session,IsTerminating 表示该方法是否可以终止 Session。默认设置 IsInitiating=true,IsTerminating=false。 我们将上面例子改一下。 [ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculate { [OperationContract(IsTerminating=true)] int Add(int a, int b); } 在客户端第二次调用 Add 方法时会抛出异常。 未处理 System.InvalidOperationException
Message="This channel cannot send any more messages because IsTerminating operation ‘Add‘ has already been called." Source="mscorlib" StackTrace: Server stack trace: 在 System.ServiceModel.Channels.ServiceChannel.PrepareCall(ProxyOperationRuntime operation, Boolean oneway, ProxyRpc& rpc) ...... 我们继续修改上面例子。 [ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculate { [OperationContract(IsInitiating = false)] int Add(int a, int b); } 第一次调用 Add 方法,触发异常。 未处理 System.InvalidOperationException
Message="ContractDescription ‘ICalculate‘ has zero IsInitiating=true operations; a contract must have at least one IsInitiating=true operation." Source="System.ServiceModel" StackTrace: 在 System.ServiceModel.Description.ContractDescription.EnsureInvariants() ...... 我们可以增加一个额外的方法来初始化会话,如下。OK,这次没问题了。 [ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculate { [OperationContract(IsInitiating=true)] void Initiate(); [OperationContract(IsInitiating=false)] int Add(int a, int b); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] public class CalculateService : ICalculate { public CalculateService() { Console.WriteLine("Constructor {0}...", this.GetHashCode()); } public void Initiate() { } public int Add(int a, int b) { Console.WriteLine(OperationContext.Current.SessionId); return a + b; } } public class WcfTest { public static void Test() { AppDomain.CreateDomain("Server").DoCallBack(delegate { ServiceHost host = new ServiceHost(typeof(CalculateService)); host.AddServiceEndpoint(typeof(ICalculate), new WSHttpBinding(), "http://localhost:8080/calc"); host.Open(); }); ChannelFactory<ICalculate> factory = new ChannelFactory<ICalculate>(new WSHttpBinding(), "http://localhost:8080/calc"); ICalculate o = factory.CreateChannel(); o.Initiate(); Console.WriteLine(o.Add(1, 2)); } } |
|