准備技術:
WCF服務契約、數據契約等基礎知識
Donet基本開發
內容概要:
三種實例激活類型
單調服務
會話服務
單例服務
實例類型
WCF支持三種實例類型:PerCall、PerSession、Single。PerCall就是單調服務會為每次客戶端的請求去分配一個新的服務實例;PerSession會為每次客戶端連接分配一個實例;Single所有的客戶端會去共享一個相同的服務實例。
WCF是通過ServiceBehavior特性中的InstanceContextMode屬性來告訴服務實例采用那種類型,InstanceContextMode屬性是一個InstanceContextMode的枚舉類型,InstanceContextMode有三個成員:PerCall(單調),PerSession(會話),Single(單例)。
單調服務
單調服務實例只存在於客戶端的調用過程中,每次客戶端的一個請求就會獲得一個新的服務實例。也就說我們在客戶端的每次調用一個方法時都會去重新返回一個新的實例給我們。下面我們看一個配置為單調服務實例的例子。
首先看我們的服務契約很簡單IBehaverContract.cs:
[ServiceContract(Name="IBehaverContract")] public interface IBehaverContract { [OperationContract(Name="TestBehavor")] string TestBehavor(); }
我們的服務實例BehaverContract.cs為:
[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");
}
}
我們在客戶端調用:
string strResult=proxy.TestBehavor(); string strResult2=proxy.TestBehavor(); Console.Write(strResult+"n"); Console.Write(strResult2);
結果如圖:
同時我們在宿主端可以監聽到:
通過實例可以清楚的看到我們調用了兩次方法,每次都是創建了一個新的實例給客戶端,也就是說單調實例不能在客戶端跟服務器端交互的時候維持某些狀態。這樣的模式的好處就是可以很好的去使用跟釋放我們比較有限的資源,如數據連接。單調服務創建於每個方法調用之前,調用完成後會立刻釋放。雖然說服務端在不斷的創建跟銷毀服務實例,但是服務端與客戶端的代理的連接是不會斷開的,這樣每次的創建的資源就比較少了。
會話服務
如果服務被配置為會話服務類型,那麼客戶端為該服務創建一個新的代理時,就會獲得一個新的而且是自己專有的,跟其他實例無關的服務實例。這種模式非常像經典的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:
[ServiceContract(Name="IBehaverContract",SessionMode=SessionMode.Required)] public interface IBehaverContract
然後我們將服務實例設置為:InstanceContextMode.PerSession:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] public class BehaverContract: ServiceContracts.IBehaverContract,IDisposable
同時我們需要在宿主中的綁定配置為支持傳輸層會話的:
<?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既可以。還看剛才的例子,我們把服務修改成:
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");
}
}
}
客戶端:
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();
}
}
}
運行結果:
服務器端:
在客戶端我們是用了兩個代理,通過服務器端的監聽以及運行的結果,我們可以清楚的看到單例模型實現的方式。