<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 跟我一起學WCF(8)——WCF中Session、實例管理詳解 ## 一、引言 由前面幾篇博文我們知道,WCF是微軟基于SOA建立的一套在分布式環境中各個相對獨立的應用進行交流(Communication)的框架,它實現了最新的基于WS-*規范。按照SOA的原則,相對獨自的業務邏輯以Service的形式進行封裝,調用者通過消息(Messaging)的方式來調用服務。對于承載某個業務功能實現的服務應該具有上下文(Context)無關性,意思就是說構造服務的操作(Operation)不應該綁定到具體的調用上下文,對于任何的調用,具有什么的樣輸入就會對應怎樣的輸出。因為SOA一個最大的目標是盡可能地實現重用,只有具有Context無關性,服務才能最大限度的重用。即從軟件架構角度理解為,一個模塊只有盡可能的獨立,即具有上下文無關性,才能被最大限度的重用。軟件體系一直在強調低耦合也是這個道理。 但是在某些場景下,我們卻希望系統為我們創建一個Session來保留Client和Service的交互的狀態,如Asp.net中Session的機制一樣,WCF也提供了對Session的支持。下面就具體看看WCF中對Session的實現。 ## 二、WCF中Session詳細介紹 ## 2.1 Asp.net的Session與WCF中的Session 在WCF中,Session屬于Service Contract的范疇,并在Service Contract定義中通過[SessionModel](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.servicecontractattribute.sessionmode(v=vs.90).aspx)參數來實現。WCF中會話具有以下幾個重要的特征: * Session都是由Client端顯示啟動和終止的。 在WCF中Client通過創建的代理對象來和服務進行交互,在支持Session的默認情況下,Session是和具體的代理對象綁定在一起,當Client通過調用代理對象的某個方法來訪問服務時,Session就被初始化,直到代理的關閉,Session則被終止。我們可以通過兩種方式來關閉Proxy:一是調用[ICommunicationObject.Close 方法](http://msdn.microsoft.com/zh-cn/library/ms195520(v=vs.110).aspx),二是調用[ClientBase&lt;TChannel&gt;.Close 方法](http://msdn.microsoft.com/zh-cn/library/ms575273(v=vs.110).aspx) 。我們也可以通過服務中的某個操作方法來初始化、或者終止Session,可以通過OperationContractAttribute的IsInitiating和[IsTerminating](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.operationcontractattribute.isterminating(v=vs.110).aspx)參數來指定初始化和終止Session的Operation。 * 在WCF會話期間,傳遞的消息按照它發送的順序被接收。 * WCF并沒有為Session的支持保存相關的狀態數據。 講到Session,做過Asp.net開發的人,自然想到的就是Asp.net中的Session。它們只是名字一樣,在實現機制上有很大的不同。Asp.net中的Session具有以下特性: * Asp.net的Session總是由服務端啟動的,即在服務端進行初始化的。 * Asp.net中的Session是無需,不能保證請求處理是有序的。 * Asp.net是通過在服務端以某種方式保存State數據來實現對Session的支持,例如保存在Web Server端的內存中。 ## 2.2 WCF中服務實例管理 對于Client來說,它實際上不能和Service進行直接交互,它只能通過客戶端創建的Proxy來間接地和Service進行交互,然而真正的調用而是通過服務實例來進行的。我們把通過Client的調用來創建最終的服務實例過程稱作激活,在.NET Remoting中包括Singleton模式、SingleCall模式和客戶端激活方式,WCF中也有類似的服務激活方式:單調服務(PerCall)、會話服務(PerSession)和單例服務(Singleton)。 * **單調服務(Percall)**:為每個客戶端請求分配一個新的服務實例。類似.NET Remoting中的SingleCall模式 * **會話服務(Persession)**:在會話期間,為每次客戶端請求共享一個服務實例,類似.NET Remoting中的客戶端激活模式。 * **單例服務(Singleton)**:所有客戶端請求都共享一個相同的服務實例,類似于.NET Remoting的Singleton模式。但它的激活方式需要注意一點:當為對于的服務類型進行Host的時候,與之對應的服務實例就被創建出來,之后所有的服務調用都由這個服務實例進行處理。 WCF中服務激活的默認方式是PerSession,但不是所有的Bingding都支持Session,比如BasicHttpBinding就不支持Session。你也可以通過下面的方式使ServiceContract不支持Session. ``` [ServiceContract(SessionMode = SessionMode.NotAllowed)] ``` 下面分別介紹下這三種激活方式的實現。 ## 三、WCF中實例管理的實現 WCF中服務激活的默認是PerSession的方式,下面就看看PerSession的實現方式。我們還是按照前面幾篇博文的方式來實現使用PerSession方式的WCF服務程序。 第一步:自然是實現我們的WCF契約和契約的服務實現。具體的實現代碼如下所示: ``` 1 // 服務契約的定義 2 [ServiceContract] 3 public interface ICalculator 4 { 5 [OperationContract(IsOneWay = true)] 6 void Increase(); 7 8 [OperationContract] 9 int GetResult(); 10 } 11 12 // 契約的實現 13 public class CalculatorService : ICalculator, IDisposable 14 { 15 private int _nCount = 0; 16 17 public CalculatorService() 18 { 19 Console.WriteLine("CalulatorService object has been created"); 20 } 21 22 // 為了看出服務實例的釋放情況 23 public void Dispose() 24 { 25 Console.WriteLine("CalulatorService object has been Disposed"); 26 } 27 28 #region ICalulator Members 29 public void Increase() 30 { 31 // 輸出Session ID 32 Console.WriteLine("The Add method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId); 33 this._nCount++; 34 } 35 36 public int GetResult() 37 { 38 Console.WriteLine("The GetResult method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId); 39 return this._nCount; 40 } 41 #endregion 42 } ``` 為了讓大家對服務對象的創建和釋放有一個直觀的認識,我特意對服務類實現了構造函數和IDisposable接口,同時在每個操作中輸出當前的Session ID。 第二步:實現服務宿主程序。這里還是采用控制臺程序作為服務宿主程序,具體的實現代碼如下所示: ``` 1 // 服務宿主程序 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 using (ServiceHost host = new ServiceHost(typeof(CalculatorService))) 7 { 8 host.Opened += delegate 9 { 10 Console.WriteLine("The Calculator Service has been started, begun to listen request..."); 11 }; 12 13 host.Open(); 14 Console.ReadLine(); 15 } 16 } 17 } ``` 對應的配置文件為: ``` <!--服務宿主的配置文件--> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name ="CalculatorBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="WCFContractAndService.CalculatorService" behaviorConfiguration="CalculatorBehavior"> <endpoint address="" binding="basicHttpBinding" contract="WCFContractAndService.ICalculator"/> <host> <baseAddresses> <add baseAddress="http://localhost:9003/CalculatorPerSession"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration> ``` 第三步:實現完了服務宿主程序,接下來自然是實現客戶端程序來訪問服務操作。這里的客戶端也是控制臺程序,具體的實現代碼如下所示: ``` 1 // 客戶端程序實現 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 // Use ChannelFactory<ICalculator> to create WCF Service proxy 7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint"); 8 Console.WriteLine("Create a calculator proxy :proxy1"); 9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel(); 10 Console.WriteLine("Invoke proxy1.Increate() method"); 11 proxy1.Increase(); 12 Console.WriteLine("Invoke proxy1.Increate() method again"); 13 proxy1.Increase(); 14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult()); 15 16 Console.WriteLine("Create another calculator proxy: proxy2"); 17 ICalculator proxy2 = calculatorChannelFactory.CreateChannel(); 18 Console.WriteLine("Invoke proxy2.Increate() method"); 19 proxy2.Increase(); 20 Console.WriteLine("Invoke proxy2.Increate() method again"); 21 proxy2.Increase(); 22 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult()); 23 24 Console.ReadLine(); 25 } 26 } ``` 客戶端對應的配置文件內容如下所示: ``` <!--客戶端配置文件--> <configuration> <system.serviceModel> <client> <endpoint address="http://localhost:9003/CalculatorPerSession" binding="basicHttpBinding" contract="WCFContractAndService.ICalculator" name="HttpEndPoint"/> </client> </system.serviceModel> </configuration> ``` 經過上面三步,我們就完成了PerSession方式的WCF程序了,下面看看該程序的運行結果。 首先,以管理員權限運行服務寄宿程序,運行成功后,你將看到如下圖所示的畫面: ![](https://box.kancloud.cn/2016-01-23_56a2eb48b3006.png) 接下來,運行客戶端對服務操作進行調用,運行成功后,你將看到服務宿主的輸出和客戶端的輸出情況如下圖所示: ![](https://box.kancloud.cn/2016-01-23_56a2eb48c191d.png) 從客戶端的運行結果可以看出,雖然我們兩次調用了Increase方法來增加_nCount的值,但是最終的運行結果仍然是0。這樣的運行結果好像與我們之前所說的WCF默認Session支持矛盾,因為如果WCF默認的方式PerSession的話,則服務實例是和Proxy綁定在一起,當Proxy調用任何一個操作的時候Session開始,從此Session將會與Proxy具有一樣的生命周期。按照這個描述,客戶端運行的結果應該是2而不是0。這里,我只能說運行結果并沒有錯,因為有圖有真相嘛,那到底是什么原因導致客戶端獲得_nCount值是0呢?其實在前面已經講到過,并不是所有的綁定都是支持Session的,上面程序的實現我們使用的basicHttpBinding,而basicHttpBinding是不支持Session方式的,所以WCF會采用PerCall的方式創建Service Instance,所以在服務端中對于每一個Proxy都有3個對象被創建,兩個是對Increase方法的調用會導致服務實例的激活,另一個是對GetResult方法的調用導致服務實例的激活。因為是PerCall方式,所以每次調用完之后,就會對服務實例進行釋放,所以對應的就有3行服務對象釋放輸出。并且由于使用的是不支持Session的binding,所以Session ID的輸出也為null。所以,上面WCF程序其實是PerCall方式的實現。 既然,上面的運行結果是由于使用了不支持Session的basicHttpBinding導致的,下面看看使用一個支持Session的Binding:wsHttpBinding來看看運行結果是怎樣的,這里的修改很簡單,只需要把宿主和客戶端的配置文件把綁定類型修改為wsHttpBinding就可以了。 ``` <!--客戶端配置文件--> <configuration> <system.serviceModel> <client> <endpoint address="http://localhost:9003/CalculatorPerSession" binding="**wsHttpBinding**" contract="WCFContractAndService.ICalculator" name="HttpEndPoint"/> </client> </system.serviceModel> </configuration> <!--服務宿主的配置文件--> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name ="CalculatorBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="WCFContractAndService.CalculatorService" behaviorConfiguration="CalculatorBehavior"> <endpoint address="" binding="**wsHttpBinding**" contract="WCFContractAndService.ICalculator"/> <host> <baseAddresses> <add baseAddress="http://localhost:9003/CalculatorPerSession"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration> ``` 現在我們再運行下上面的程序來看看此時的執行結果,具體的運行結果如下圖所示: ![](https://box.kancloud.cn/2016-01-23_56a2eb48d9d9e.png) 從上面的運行結果可以看出,此時兩個Proxy的運行結果都是2,可以看出此時服務激活方式采用的是PerSession方式。此時對于服務端就只有兩個服務實例被創建了,并且對于每個服務實例具有相同的Session ID。 另外由于Client的Proxy還依然存在,服務實例也不會被回收掉,從上面服務端運行的結果也可以證實這點,因為運行結果中沒有對象唄Disposable的輸出。你可以在客戶端顯式調用ICommunicationObject.Close方法來顯式關閉掉Proxy,在客戶端添加對Proxy的顯示關閉代碼,此時客戶端的代碼修改為如下所示: ``` 1 // 客戶端程序實現 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 // Use ChannelFactory<ICalculator> to create WCF Service proxy 7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint"); 8 Console.WriteLine("Create a calculator proxy :proxy1"); 9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel(); 10 Console.WriteLine("Invoke proxy1.Increate() method"); 11 proxy1.Increase(); 12 Console.WriteLine("Invoke proxy1.Increate() method again"); 13 proxy1.Increase(); 14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult()); 15 **(proxy1 as ICommunicationObject).Close(); // 顯示關閉Proxy** 16 17 Console.WriteLine("Create another calculator proxy: proxy2"); 18 ICalculator proxy2 = calculatorChannelFactory.CreateChannel(); 19 Console.WriteLine("Invoke proxy2.Increate() method"); 20 proxy2.Increase(); 21 Console.WriteLine("Invoke proxy2.Increate() method again"); 22 proxy2.Increase(); 23 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult()); 24 **(proxy2 as ICommunicationObject).Close();** 25 26 Console.ReadLine(); 27 } 28 } ``` 此時,服務對象的Dispose()方法將會調用,此時服務端的運行結果如下圖所示: ![](https://box.kancloud.cn/2016-01-23_56a2eb48edc84.png) 上面演示了默認支持Session的情況,下面我們修改服務契約使之不支持Session,此時只需要知道ServiceContract的SessionMode為NotAllowed即可。 ``` [ServiceContract**(SessionMode= SessionMode.NotAllowed)**] // 是服務契約不支持Session public interface ICalculator { [OperationContract(IsOneWay = true)] void Increase(); [OperationContract] int GetResult(); } ``` 此時,由于服務契約不支持Session,此時服務激活方式采用的仍然是PerCall。運行結果與前面采用不支持Session的綁定的運行結果一樣,這里就不一一貼圖了。 除了通過顯式修改ServiceContract的SessionMode來使服務契約支持或不支持Session外,還可以定制操作對Session的支持。定制操作對Session的支持可以通過OperationContract的IsInitiating和InTerminating屬性設置。 ``` 1 // 服務契約的定義 2 [ServiceContract(SessionMode= SessionMode.Required)] // 顯式使服務契約支持Session 3 public interface ICalculator 4 { 5 // IsInitiating:該值指示方法是否實現可在服務器上啟動會話(如果存在會話)的操作,默認值是true 6 // IsTerminating:獲取或設置一個值,該值指示服務操作在發送答復消息(如果存在)后,是否會導致服務器關閉會話,默認值是false 7 [OperationContract(IsOneWay = true, IsInitiating =true, IsTerminating=false )] 8 void Increase(); 9 10 [OperationContract(IsInitiating = true, IsTerminating = true)] 11 int GetResult(); 12 } ``` 在上面代碼中,對兩個操作都設置InInitiating的屬性為true,意味著調用這兩個操作都會啟動會話,而把GetResult操作的IsTerminating設置為true,意味著調用完這個操作后,會導致服務關閉掉會話,因為在Session方式下,Proxy與Session有一致的生命周期,所以關閉Session也就是關閉proxy對象,所以如果后面再對proxy對象的任何一個方法進行調用將會導致異常,下面代碼即演示了這種情況。 ``` 1 // 客戶端程序實現 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 // Use ChannelFactory<ICalculator> to create WCF Service proxy 7 ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("HttpEndPoint"); 8 Console.WriteLine("Create a calculator proxy :proxy1"); 9 ICalculator proxy1 = calculatorChannelFactory.CreateChannel(); 10 Console.WriteLine("Invoke proxy1.Increate() method"); 11 proxy1.Increase(); 12 Console.WriteLine("Invoke proxy1.Increate() method again"); 13 proxy1.Increase(); 14 Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult()); 15 **try 16 { 17 proxy1.Increase(); // session關閉后對proxy1.Increase方法調用將會導致異常 18 } 19 catch (Exception ex) // 異常捕獲 20 { 21 Console.WriteLine("在Session關閉后調用Increase方法失敗,錯誤信息為:{0}", ex.Message); 22 }** 23 24 Console.WriteLine("Create another calculator proxy: proxy2"); 25 ICalculator proxy2 = calculatorChannelFactory.CreateChannel(); 26 Console.WriteLine("Invoke proxy2.Increate() method"); 27 proxy2.Increase(); 28 Console.WriteLine("Invoke proxy2.Increate() method again"); 29 proxy2.Increase(); 30 Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult()); 31 32 Console.ReadLine(); 33 } 34 } ``` 此時運行結果也驗證我們上面的分析,客戶端和服務端的運行結果如下圖所示: ![](https://box.kancloud.cn/2016-01-23_56a2eb490d13e.png) 上面演示了PerSession和PerCall的兩種服務對象激活方式,下面看看Single的激活方式運行的結果。首先通過ServiceBehavior的InstanceContextMode屬性顯式指定激活方式為Single,由于[ServiceBehaviorAttribute](http://msdn.microsoft.com/zh-cn/library/System.ServiceModel.ServiceBehaviorAttribute(v=vs.110).aspx)特性只能應用于類上,所以把該特性應用于CalculatorService類上,此時服務實現的代碼如下所示: ``` // 契約的實現 // ServiceBehavior屬性只能應用在類上 **[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] // 顯示指定PerSingle方式** public class CalculatorService : ICalculator, IDisposable { private int _nCount = 0; public CalculatorService() { Console.WriteLine("CalulatorService object has been created"); } // 為了看出服務實例的釋放情況 public void Dispose() { Console.WriteLine("CalulatorService object has been Disposed"); } #region ICalulator Members public void Increase() { // 輸出Session ID Console.WriteLine("The Add method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId); this._nCount++; } public int GetResult() { Console.WriteLine("The GetResult method is invoked and the current session ID is: {0}", OperationContext.Current.SessionId); return this._nCount; } #endregion } ``` 此時運行服務宿主的輸出結果如下圖所示: ![](https://box.kancloud.cn/2016-01-23_56a2eb49265d7.png) 從運行結果可以看出,對于Single方式,服務實例在服務類型被寄宿的時候就已經創建了,對于PerCall和PerSession方式而是在通過Proxy調用相應的服務操作之后,服務實例才開始創建的。下面運行客戶端程序,你將看到如下圖所示的運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb493b301.png) 此時,第二個Proxy返回的結果是4而不是2,這是因為采用Single方式只存在一個服務實例,所有的調用狀態都將保留,所以_nCount的值在原來的基礎上繼續累加。 ## 四、總結 到這里,本文的分享就結束了,本文主要分享了WCF中實例管理的實現。從WCF的實例實現可以看出,WCF實例實現是借鑒了.NET Remoting中實例實現,然后分別分享了服務實例三種激活方式在WCF中的實現,并通過對運行結果進行對比來讓大家理解它們之間的區別。 本文所以源碼:[WCFInstanceManager.zip](http://files.cnblogs.com/zhili/WCFInstanceManager.zip)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看