InstanceContextMode為服務端實現服務契約類的執行個體模式,有三種類型,分別為:PerCall-每次服務作業調用建立一次,
調用完後進行銷毀;PerSession-同一個會話期間建立一次,用戶端代理第一次操作(IsInitiating = true)調用建立,
調用代理的Close方法銷毀或者調用IsTerminating服務作業銷毀;Single-服務只會建立一次,服務開始時建立,服務完
成時銷毀
SessionMode是用戶端代理與伺服器之間的會話模式,同樣也有三種類型:Allowed-允許會話、NotAllowed-不允許會話、Required-要求會話(需要有支援會話的Binding支援,WsHttpBinding、NetTcpBinding等)
本文是對不同執行個體模式下的不同會話模式進行的測試比較,代碼來自attach的文章。
測試代碼及設定檔說明服務契約:
[ServiceContract] public interface ICalculator { [OperationContract(IsOneWay = true)] void Add(double x); [OperationContract] double GetResult(); }服務契約實現:
[ServiceBehavior] class CalculatorService :appledou.Test.WCF.Contract.ICalculator, IDisposable { private double _result; #region ICalculator Members public void Add(double x) { Console.WriteLine("The Add method is invoked and the current SessionID is: {0}", OperationContext.Current.SessionId); this._result += x; } public double GetResult() { Console.WriteLine("The GetResult method is invoked and the current SessionID is: {0}\r\n", OperationContext.Current.SessionId); return this._result; } #endregion public CalculatorService() { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("Calculator object has been created"); Console.ResetColor(); }
#region IDisposable Members public void Dispose() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("The IDisposable.Dispose method is invoked\r\n"); Console.ResetColor(); } #endregion }
Add方法的參數累加在類變數_result裡面,最後通過GetResult返回,同時在每個方法裡面輸出當前的SessionID,在構造
函數和Dispose方法輸出調用資訊,方便觀測服務端行為。
伺服器端設定檔:
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="CalculatorBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <wsHttpBinding> <binding name="wsHttp"> <reliableSession enabled="true" inactivityTimeout="00:00:30"/> </binding> </wsHttpBinding> </bindings> <services> <service name="appledou.Test.WCF.Service.CalculatorService" behaviorConfiguration="CalculatorBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8888/"/> </baseAddresses> </host> <endpoint address="Calculator" binding="wsHttpBinding" bindingConfiguration="wsHttp" contract="appledou.Test.WCF.Contract.ICalculator"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> </system.serviceModel>
Binding為支援會話的wsHttpBinding
用戶端調用代碼:
ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("Calculator");Console.WriteLine("Create a calculator proxy: proxy1");ICalculator proxy1 = channelFactory.CreateChannel();Console.WriteLine("Invocate proxy1.Add(1)");proxy1.Add(1);Console.WriteLine("Invocate proxy1.Add(2)");proxy1.Add(2);Console.WriteLine("The result return via proxy1.GetResult() is : {0}\r\n", proxy1.GetResult());(proxy1 as ICommunicationObject).Close();
//---------------------------------Console.WriteLine("Create a calculator proxy: proxy2");ICalculator proxy2 = channelFactory.CreateChannel();Console.WriteLine("Invocate proxy2.Add(1)");proxy2.Add(1);Console.WriteLine("Invocate proxy2.Add(2)");proxy2.Add(2);Console.WriteLine("The result return via proxy2.GetResult() is : {0}", proxy2.GetResult());(proxy2 as ICommunicationObject).Close();
用戶端設定檔:
<system.serviceModel> <client> <endpoint name="Calculator" address="http://localhost:8888/Calculator" binding="wsHttpBinding" bindingConfiguration="wsHttp" contract="appledou.Test.WCF.Contract.ICalculator"/> </client> <bindings> <wsHttpBinding> <binding name="wsHttp"> <reliableSession enabled="true" inactivityTimeout="00:00:30"/> </binding> </wsHttpBinding> </bindings> </system.serviceModel>
PerCall下的會話模式
單調服務的一個最重要優勢在於它能夠節省資源,支援系統的延展性。由於服務執行個體的生命週期只存在於一次調用期間,特別對於那些持有昂貴資源的服務執行個體而言,這種方式可以有效地提高系統效能。而且,銷毀服務執行個體時,WCF不會斷開與用戶端(通過用戶端的代理)的串連,這比建立執行個體與串連所消耗的資源要少得多(引用wayfarer的部落格)
首先設定服務執行個體為PerCall
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]class CalculatorService :appledou.Test.WCF.Contract.ICalculator, IDisposable
SessionMode為Required
設定服務契約
[ServiceContract(SessionMode = SessionMode.Required)]public interface ICalculator
用戶端輸出:
可以看到GetResult並沒有返回前兩次累加的值,因為執行個體模式是PerCall
服務端輸出:
從服務端輸出顯示更能說明問題,每次方法調用首先建立服務執行個體(藍色字型輸出),調用完成後調用Dispose方法釋放資源(黃色輸出),當記憶體回收時銷毀服務執行個體。同時還可以看到一個有趣的現象就是雖然每次調用都會建立新的執行個體,但是在同一個用戶端代理關閉之前SessionID是一致的(前三次-proxy1的SessionID一致,後三次-proxy2的SessionID一致),正好應了銷毀服務執行個體時,WCF不會斷開與用戶端(通過用戶端的代理)的串連。
SessionMode為NotAllowed
設定服務契約
[ServiceContract(SessionMode = SessionMode.NotAllowed)]public interface ICalculator
用戶端輸出:
因為服務執行個體為PerCall,所以同Required的SessionMode並沒有區別
而服務端則沒有了SessionID,其它的都一樣。
SessionMode為Allowed
SessionMode的Allowed和Required的區別就是,Allowed允許有Session,也就是說如果是支援Session的Binding那麼效果就和Required一樣,否則效果跟NoAllowed一樣。而Required必須要求是支援Session的Binding
PerSession下的會話模式
PerSession從字面上就可以理解為,對於每一個用戶端代理或者說是會話建立一個執行個體,代理第一次調用服務契約操作建立執行個體,當代理調用Close方法或者Terminating方法([OperationContract(IsTerminating = true)])結束會話並釋放服務執行個體,同時服務執行個體可以被記憶體回收。
設定服務執行個體為PerSession
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]class CalculatorService :appledou.Test.WCF.Contract.ICalculator, IDisposable
SessionMode為Required
設定服務契約
[ServiceContract(SessionMode = SessionMode.Required)]public interface ICalculator
用戶端輸出:
可以看到在代理Close方法執行之前的GetResult方法返回了前兩次Add累加的值:
Console.WriteLine("The result return via proxy1.GetResult() is : {0}\r\n", proxy1.GetResult());(proxy1 as ICommunicationObject).Close();
服務端輸出:
在服務端當用戶端第一次調用Add方法的時候建立執行個體,而調用Close完成後調用Dispose釋放資源。同時在這期間的
SessionID是一致的,說明他們在同一個會話中執行的。
SessionMode為NotAllowed
設定服務契約
[ServiceContract(SessionMode = SessionMode.NotAllowed)]public interface ICalculator
用戶端輸出:
服務端輸出:
無論是服務端或者用戶端都跟PerCall的NotAllowed一樣,我想這也好理解。因為服務執行個體的生命期是PerSession,而服務契約又不允許Session,所以服務執行個體的生命期就是PerCall之間,並且沒有SessionID。
可是這樣的組合在什麼情況下才會有呢?設想如果CalculatorService服務類又實現了一個服務契約並且SessionMode為Required或者Allowed,那麼這個時候CalculatorService的InstanceContextMode為PerSession就有意義了,對於SessionMode為NotAllowed的服務契約會跟PerCall一樣,但是對於SessionMode為Required或者Allowed的服務契約就是真正的PerSession了。如:
前三個方法調用,每次建立並且完成調用後釋放對象,而後三次調用則在一個會話之內。這是因為同一個服務類CalculatorService
又實現了另一個服務契約ICalculatorV2,並且它要求SessionMode為Required,而原來的服務契約ICalculator的
SessionMode為NotAllowed:
[ServiceContract(SessionMode = SessionMode.Required)] public interface ICalculatorV2 { [OperationContract(IsOneWay = true, IsInitiating = true)] void Add2(double x); [OperationContract(IsTerminating = false)] double GetResult2(); }
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] class CalculatorService : appledou.Test.WCF.Contract.ICalculator, appledou.Test.WCF.Contract.ICalculatorV2, IDisposable
用戶端調用代碼:
ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("Calculator");Console.WriteLine("Create a calculator proxy of ICalculatorV1: proxy1");
ICalculator proxy1 = channelFactory.CreateChannel();Console.WriteLine("Invocate proxy1.Add(1)");proxy1.Add(1);Console.WriteLine("Invocate proxy1.Add(2)");proxy1.Add(2);Console.WriteLine("The result return via proxy1.GetResult() is : {0}\r\n", proxy1.GetResult());(proxy1 as ICommunicationObject).Close();ChannelFactory<ICalculatorV2> channelFactory2 = new ChannelFactory<ICalculatorV2>("ICalculatorV2");Console.WriteLine("Create a calculator proxy2 of ICalculatorV2: proxy2");ICalculatorV2 proxy2 = channelFactory2.CreateChannel();Console.WriteLine("Invocate proxy2.Add2(1)");proxy2.Add2(1);Console.WriteLine("Invocate proxy2.Add2(2)");proxy2.Add2(2);Console.WriteLine("The result return via proxy2.GetResult() is : {0}\r\n", proxy2.GetResult2());(proxy2 as ICommunicationObject).Close();
用戶端輸出:
SessionMode為Allowed
同樣道理在支援Session的Binding下面同Required一樣,在不支援Session的Binding下面同NotAllowed一樣。
Single下的會話模式
Single模式下的服務只建立一次,而且是在服務開啟的時候,這個模式比較好理解,下面看看Single模式下SessionMode的不同情況
首先設定服務執行個體為Single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]class CalculatorService :appledou.Test.WCF.Contract.ICalculator, IDisposable
SessionMode為Required
設定服務契約
[ServiceContract(SessionMode = SessionMode.Required)]public interface ICalculator
用戶端輸出:
可以看到在兩次不同的代理調用後,伺服器返回的值一直在累加,這說明雖然用戶端代理不一樣,但是服務端調用的是同一個執行個體。
服務端輸出:
首先在服務開啟後,Single類型的服務模型就被建立,其次 在同一個代理調用期間SessionID是一致的。
SessionMode為NotAllowed
設定服務契約
[ServiceContract(SessionMode = SessionMode.NotAllowed)]public interface ICalculator
用戶端輸出:
沒什麼變化,同Required模式下一樣
服務端輸出:
而服務端如NotAllowed所願沒有了SessionID,其它都一樣
SessionMode為Allowed
這個就不用多說了
小結
本文是在WsHttpBinding的基礎之上進行的測試,我想同樣適用於其它支援Session的Binding,如NetTcpBinding。希望本文能協助你理清InstanceContextMode和SessionMode之間不同取值的情況下對服務執行個體的影響。