對于Client來說,它實際上不能和Service進行直接互動,它隻能通過用戶端建立的Proxy來間接地和Service進行互動,然而真正的調用而是通過服務執行個體來進行的。我們把通過Client的調用來建立最終的服務執行個體過程稱作激活,在.NET Remoting中包括Singleton模式、SingleCall模式和用戶端激活方式,WCF中也有類似的服務激活方式:單調服務(PerCall)、會話服務(PerSession)和單例服務(Singleton)。
一、了解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執行個體管理
- 單調服務(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已經啟動,按按Enter鍵終止服務!"); };
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("登出成功!");
}
}
}
}
}
如果同時打開多個用戶端程式,并輸入相同的使用者名,隻要有一個登入成功或登入成功後不登出,其餘的均會登入不上,報錯!效果如下圖示:
![]() | |
當然也可以利用其它激活方式實作更多功能,在此就不再重述,原理相同!
本文參考與引用了以下作者的文章:
跟我一起學WCF(8)——WCF中Session、執行個體管理詳解