在本次實驗中你將會體會到三種不同的執行個體管理員模式。開啟Instance Management檔案夾下的InstanceManagement.sln解決方案。解決方案中包含了一個簡單的伺服器端和用戶端。它們都被配置為了BasicHttpBinding。
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "basicHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
為了監視執行個體的建立、回收和狀態,service端的代碼在它的三個方法:建構函式、MyMethod()和Dispose()中都會顯示一個MessageBox來表明狀態。另外service中有一個m_Counter的int型欄位,我們可以通過觀察它來得知我們是不是獲得了一個新的執行個體。
[ServiceContract]
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()方法:
void OnCall(object sender,EventArgs e)
{
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即可:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyService : IMyContract, IDisposable
{……}
Per-Session模式
接下來,我們將會把服務配置為每個用戶端都會關聯到一個特定的服務,類似於經典的C/S模式。將伺服器屬性InstanceContextMode修改為InstanceContextMode.PerSession:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class MyService : IMyContract, IDisposable
{……}
你會看到,服務仍然是Per-Call的,並沒有session。因為要將session設定為contract的一部分,這樣才會有效果。開啟MyService.cs檔案,將service contract的SessionMode屬性設定為
SessionMode.Required:
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{……}
同樣,在client端的Proxy.cs檔案中也需要做這樣的定義。
編譯器並運行,你會得到如下的異常:
這是因為BasicHttpBinding不支援session。開啟app.config檔案,將binding改為WSHttpBinding:
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
在client端也做同樣的設定。
編譯後運行程式。這一次你將只會得到一個伺服器執行個體,counter的數量會變為2,執行個體也僅僅是在client端關閉後調用一次。啟動兩個client程式觀察一下呢?
單一實例模式
第三種是單一實例模式(Singleton),即所有client端都串連到同一個執行個體上。將伺服器屬性InstanceContextMode修改為InstanceContextMode.Single:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class MyService : IMyContract, IDisposable
{……}
編譯並運行程式,你將會看到,在沒有任何client調用的時候伺服器端的建構函式已經執行了。點擊client端的button兩次,counter的數量應該為4。關閉client程式,Dispose方法仍然沒有被調用。
單一實例模式是不需要session的,將SessionMode=SessionMode.Required去掉再試試看呢。