天天看點

WCF會話(Session)與執行個體(Instance)管理

對于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會話(Session)與執行個體(Instance)管理
WCF會話(Session)與執行個體(Instance)管理

當然也可以利用其它激活方式實作更多功能,在此就不再重述,原理相同!

本文參考與引用了以下作者的文章:

跟我一起學WCF(8)——WCF中Session、執行個體管理詳解