在本次实验中你将会体会到三种不同的实例管理模式。打开Instance Management文件夹下的InstanceManagement.sln解决方案。解决方案中包含了一个简单的服务器端和客户端。它们都被配置为了BasicHttpBinding。
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "basicHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
为了监视实例的创建、回收和状态,service端的代码在它的三个方法:构造函数、MyMethod()和Dispose()中都会显示一个MessageBox来表明状态。另外service中有一个m_Counter的int型字段,我们可以通过观察它来得知我们是不是获得了一个新的实例。
interface IMyContract
{
[OperationContract]
void MyMethod();
}
class MyService : IMyContract,IDisposable
{
int m_Counter = 0;
public MyService()
{
MessageBox.Show("Counter = " + m_Counter,"MyService.MyService()");
}
public void MyMethod()
{
m_Counter++;
MessageBox.Show("Counter = " + m_Counter,"MyService.MyMethod()");
string sessionID = OperationContext.Current.SessionId;
Trace.WriteLine("Service Session ID: " + (sessionID??"None"));
}
public void Dispose()
{
MessageBox.Show("","MyService.Dispose()");
}
}
MyMethod方法同样还记录了session ID。
Client端的代码创建了一个代理来调用MyMethod()方法:
{
MyContractClient proxy = new MyContractClient();
proxy.MyMethod();
proxy.MyMethod();
IContextChannel channel = proxy.InnerChannel;
string sessionID = channel.SessionId;
Trace.WriteLine("Client Session ID: " + (sessionID??"None"));
proxy.Close();
}
Client端同样记录了session ID。编译并运行程序。
Per-Call模式
Per-Call意味着对于两次client端的调用,你将会得到两个不同的实例。也就是说即使client端两次调用使用的是相同的代理,每一次调用服务器端实例构造函数都会被调用一次。m_Counter的值始终都是1。在函数返回后Dispose()方法都会在后台被执行。注意到,在Debug窗口中我们可以观察到session ID都是没有的。
我们也可以显示的将服务配置为Per-Call类型。只需要将InstanceContextMode属性设置为InstanceContextMode.PerCall即可:
class MyService : IMyContract, IDisposable
{……}
Per-Session模式
接下来,我们将会把服务配置为每个客户端都会关联到一个特定的服务,类似于经典的C/S模式。将服务器属性InstanceContextMode修改为InstanceContextMode.PerSession:
class MyService : IMyContract, IDisposable
{……}
你会看到,服务仍然是Per-Call的,并没有session。因为要将session设置为contract的一部分,这样才会有效果。打开MyService.cs文件,将service contract的SessionMode属性设置为
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{……}
同样,在client端的Proxy.cs文件中也需要做这样的定义。
编译程序并运行,你会得到如下的异常:
这是因为BasicHttpBinding不支持session。打开app.config文件,将binding改为WSHttpBinding:
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
在client端也做同样的设置。
编译后运行程序。这一次你将只会得到一个服务器实例,counter的数量会变为2,实例也仅仅是在client端关闭后调用一次。启动两个client程序观察一下呢?
单实例模式
第三种是单实例模式(Singleton),即所有client端都连接到同一个实例上。将服务器属性InstanceContextMode修改为InstanceContextMode.Single:
class MyService : IMyContract, IDisposable
{……}
编译并运行程序,你将会看到,在没有任何client调用的时候服务器端的构造函数已经执行了。点击client端的button两次,counter的数量应该为4。关闭client程序,Dispose方法仍然没有被调用。
单实例模式是不需要session的,将SessionMode=SessionMode.Required去掉再试试看呢。