准备技术:
WCF服务契约、数据契约等基础知识
Donet基本开发
内容概要:
三种实例激活类型
单调服务
会话服务
单例服务
实例类型
WCF支持三种实例类型:PerCall、PerSession、Single。PerCall就是单调服务会为每次客户端的请求去分配一个新的服务实例;PerSession会为每次客户端连接分配一个实例;Single所有的客户端会去共享一个相同的服务实例。
WCF是通过ServiceBehavior特性中的InstanceContextMode属性来告诉服务实例采用那种类型,InstanceContextMode属性是一个InstanceContextMode的枚举类型,InstanceContextMode有三个成员:PerCall(单调),PerSession(会话),Single(单例)。
单调服务
单调服务实例只存在于客户端的调用过程中,每次客户端的一个请求就会获得一个新的服务实例。也就说我们在客户端的每次调用一个方法时都会去重新返回一个新的实例给我们。下面我们看一个配置为单调服务实例的例子。
首先看我们的服务契约很简单IBehaverContract.cs:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
[ServiceContract(Name = "IBehaverContract")]
public interface IBehaverContract
{
[OperationContract(Name = "TestBehavor")]
string TestBehavor();
}
我们的服务实例BehaverContract.cs为:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCal)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
{
private int _Count = 0;
public BehaverContract()
{
Console.Write("BehaverContract.BehaverContract()\n");
}
public string TestBehavor()
{
_Count++;
return String.Format("The number is :{0}", _Count);
}
public void Dispose()
{
Console.Write("BehaverContract.Dispose()\n");
}
}
我们在客户端调用:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
string strResult= proxy.TestBehavor();
string strResult2 = proxy.TestBehavor();
Console.Write(strResult+"\n");
Console.Write(strResult2);
结果如图:
![](https://www.cnblogs.com/images/cnblogs_com/henllyee/run1.JPG)
同时我们在宿主端可以监听到:
通过实例可以清楚的看到我们调用了两次方法,每次都是创建了一个新的实例给客户端,也就是说单调实例不能在客户端跟服务器端交互的时候维持某些状态。这样的模式的好处就是可以很好的去使用跟释放我们比较有限的资源,如数据连接。单调服务创建于每个方法调用之前,调用完成后会立刻释放。虽然说服务端在不断的创建跟销毁服务实例,但是服务端与客户端的代理的连接是不会断开的,这样每次的创建的资源就比较少了。
会话服务
如果服务被配置为会话服务类型,那么客户端为该服务创建一个新的代理时,就会获得一个新的而且是自己专有的,跟其他实例无关的服务实例。这种模式非常像经典的c/s模式,该实例一直到客户端不在需要使用时才会去释放。PerSession模式也就是相当于remoting中的客户端激活模式。
在配置私有会话时候,我们需要在服务上配置InstanceContextMode为PerSession,PerSession也是InstanceContextMode属性的默认值。配置了这样的属性时,并不能确保我们的服务就是私有会话的,因为私有会话模式需要维持某一个客户端到服务器端的状态,所以需要客户端到服务端的连接是持久的。那么Http协议的无连接就不能维持私有会话了,如果要基于Http的传输WCF提供了wsHttpBinding来模仿一个持久的传输。
在传输层的会话的同时,契约的配置也是需要的,因为在运行时WCF需要知道服务是否启用了会话。通过ServiceContract的SessionMode属性,我们可以来设定我们的契约是否支持会话。
SessionMode枚举有三个成员:Allowed、Required、NotAllowed。SessionMode属性的默认值为SessionMode. Allowed。当我们设置为Allowed时只能代表是允许,只有当绑定是一个传输层会话时才能采用会话服务的执行。SessionMode.Required要求必须使用传输层会话。SessionMode.NotAllowed禁止使用传输层会话,也就不能使用应用层会话了,也就是说即使我们将InstanceContextMode属性配置为InstanceContextMode.PerSession时,它总会采用PerCall去执行。
我们还看刚才那个的那个例子,只是把相应的配置给修改下。首先我们将服务契约的SessionMode设置为SessionMode.Requried:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
[ServiceContract(Name = "IBehaverContract",SessionMode=SessionMode.Required)]
public interface IBehaverContract
然后我们将服务实例设置为:InstanceContextMode.PerSession:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
同时我们需要在宿主中的绑定配置为支持传输层会话的:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="Service.BehaverContract" behaviorConfiguration="behavior">
<endpoint binding="wsHttpBinding"
contract="ServiceContracts.IBehaverContract"
address="BehaverContract"
bindingConfiguration="HttpSession">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="behavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="HttpSession">
<reliableSession enabled="true"/>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
客户端的调用还是一样的,我们看到如下的结果:
在我们启用了私有会话的时候,服务端跟客户端都能获得此次会话的一个唯一的会话ID。服务可以在操作上下文中去获取这个ID(OperationContext.Current.SessionId),客户端可以通过InnerChannel.SessionId去获取。服务端跟每个客户端的会话维持就是通过这个SessionId去关联的。
会话一般是在客户端关闭了代理时才会结束,但是客户端可以强行的终止会话。每次会话空闲超时时间为10分钟。我们可以通过绑定配置ReliableSession的InactivityTimeout属性去设置超时时间。
单例服务
单例服务就是说所有的客户端都将连接相同的实例,单例服务的实例生命周期是无限的。配置时,我们只需要在将InstanceContextMode属性设置为InstanceContextMode.Single既可以。还看刚才的例子,我们把服务修改成:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
{
private int _Count = 0;
public BehaverContract()
{
Console.Write("BehaverContract.BehaverContract()\n");
}
public string TestBehavor()
{
_Count++;
return String.Format("The number is :{0}", _Count);
}
public void Dispose()
{
Console.Write("BehaverContract.Dispose()\n");
}
}
}
客户端:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Client
{
class Program
{
static void Main(string[] args)
{
BehaverContract.BehaverContractClient proxy = new Client.BehaverContract.BehaverContractClient();
proxy.Open();
string strResult= proxy.TestBehavor();
BehaverContract.BehaverContractClient proxy2 = new Client.BehaverContract.BehaverContractClient();
string strResult2 = proxy2.TestBehavor();
Console.Write(strResult+"\n");
Console.Write(strResult2);
//Console.Write("\nSession ID is {0}", proxy.InnerChannel.SessionId);
proxy.Close();
Console.Read();
}
}
}
运行结果:
![](https://www.cnblogs.com/images/cnblogs_com/henllyee/run4.JPG)
服务器端:
![](https://www.cnblogs.com/images/cnblogs_com/henllyee/run5.JPG)
在客户端我们是用了两个代理,通过服务器端的监听以及运行的结果,我们可以清楚的看到单例模型实现的方式。