一、理解Session
1.Session的作用:保留Client和Service之間交互的狀態,確保Client與Service之間交互唯一性(SessionId),即:多個Client同時訪問Service,Service能夠區別;
2.ASP.NET Session 與 WCF Session區別:
在WCF中,Session屬於Service Contract的范疇,並在Service Contract定義中通過SessionModel參數來實現。WCF中會話具有以下幾個重要的特征:
- Session都是由Client端顯示啟動和終止的。
在WCF中Client通過創建的代理對象來和服務進行交互,在支持Session的默認情況下,Session是和具體的代理對象綁定在一起,當Client通過調用代理對象的某個方法來訪問服務時,Session就被初始化,直到代理的關閉,Session則被終止。我們可以通過兩種方式來關閉代理:一是調用ICommunicationObject.Close 方法,二是調用ClientBase<TChannel>.Close 方法 。我們也可以通過服務中的某個操作方法來初始化、或者終止Session,可以通過OperationContractAttribute的IsInitiating和IsTerminating參數來指定初始化和終止Session的Operation。
- 在WCF會話期間,傳遞的消息按照它發送的順序被接收。
- WCF並沒有為Session支持保存相關的狀態數據。
而Asp.net中的Session具有以下特性:
- Asp.net的Session總是由服務端啟動的,即在服務端進行初始化的。
- Asp.net中的Session是無序的,不能保證請求處理是有序的。
- Asp.net是通過在服務端以某種方式保存State數據來實現對Session的支持,例如保存在Web Server端的內存中。
二、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的時候,與之對應的服務實例就被創建出來,之后所有的服務調用都由這個服務實例進行處理。
注意:
1.WCF中服務激活的默認方式是PerSession,但不是所有的Bingding都支持Session,比如BasicHttpBinding就不支持Session。
2.通過在服務契約接口上ServiceContract(SessionMode = 會話模式)來顯式設置會話模式,禁用會話模式,可設為:SessionMode.NotAllowed
3.通過在Service實現類上ServiceBehavior(InstanceContextMode=激活方式)來顯式設置服務實例激活方式
三、運用WCF 的單例服務(Singleton)及會話模式,實現系統同一時間只能允許同一用戶名登錄(即:單次登錄),代碼如下:
1.定義服務契約及創建服務類
using System.ServiceModel; namespace WcfServiceLibrary1 { [ServiceContract(SessionMode = SessionMode.Required)] public interface ILogin { [OperationContract] string Login(string username, string password); [OperationContract(IsOneWay=true)] void Logout(); } } using System.Collections.Generic; using System.ServiceModel; namespace WcfServiceLibrary1 { [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class LoginService:ILogin { private Dictionary<string,string> loginUsers; public LoginService() { this.loginUsers = new Dictionary<string, string>(); } public string Login(string username, string password) { if (!string.IsNullOrEmpty(username) && password == "123456") { if (!this.loginUsers.ContainsValue(username)) { this.loginUsers.Add(OperationContext.Current.SessionId,username); return null; } else { return string.Format("用戶{0}已在其它地方有登錄,同一時間不允許同一用戶重復登錄!", username); } } else { return "用戶名或密碼錯誤!"; } } public void Logout() { this.loginUsers.Remove(OperationContext.Current.SessionId); } } }
2.創建宿主程序
CONFIG配置文件:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <behaviors> <behavior name="LoginServicemetadatabehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </behaviors> <services> <service name="WcfServiceLibrary1.LoginService" behaviorConfiguration="LoginServicemetadatabehavior"> <endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.ILogin"></endpoint> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:10900/LoginService"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
代碼部份:
using System; using WcfServiceLibrary1; using System.ServiceModel; using System.ServiceModel.Description; namespace ConsoleApplicationHost { class Program { static void Main(string[] args) { BuildLoginServiceHostByConfig(); } static void BuildLoginServiceHostByConfig() { using (ServiceHost host = new ServiceHost(typeof(LoginService))) { host.Opened += (s, e) => { Console.WriteLine("LoginService已經啟動,按按回車鍵終止服務!"); }; host.Open(); Console.ReadLine(); } } } }
3.在客戶端程序調用WCF服務
首先添加並引用WCF服務,VS自動生成WCF服務相關的接口與代理類,這里是:LoginClient
然后就可以直接使用LoginClient來調用WCF服務相關方法,代碼如下:
using System; using System.ServiceModel; using WcfServiceLibrary1; namespace ConsoleApplicationClient { class Program { static void Main(string[] args) { CallLoginService(); Console.WriteLine("按任意鍵結束。"); Console.Read(); } static void CallLoginService() { using (LoginServices.LoginClient proxy = new LoginServices.LoginClient()) { Console.Write("請輸入用戶名:"); string input1 = Console.ReadLine(); Console.Write("請輸入密碼:"); string input2 = Console.ReadLine(); string loginResult = proxy.Login(input1, input2); if (!string.IsNullOrEmpty(loginResult)) { Console.WriteLine(loginResult); return; } Console.WriteLine("恭喜你,登錄成功!"); Console.Write("若需登出,請輸入Y:"); string input3 = Console.ReadLine(); if (input3 == "Y") { proxy.Logout(); Console.WriteLine("登出成功!"); } } } } }
如果同時打開多個客戶端程序,並輸入相同的用戶名,只要有一個登錄成功或登錄成功后不登出,其余的均會登錄不上,報錯!效果如下圖示:
![]() |
![]() |
當然也可以利用其它激活方式實現更多功能,在此就不再重述,原理相同!
本文參考與引用了以下作者的文章: