執行個體初始化

初始化範例 透過定義介面 ,擴充了 IObjectControl 範例,該介面可自訂物件的初始化方式,藉由啟用及停用來達成。 用戶端會叫用將物件傳回集區的方法,以及不將物件傳回集區的方法。

注意

此範例的安裝程序與建置指示位於本主題的結尾。

擴充點

建立 Windows Communication Foundation (WCF) 延伸項目的第一個步驟是決定要使用的擴充點。 在 WCF 中, EndpointDispatcher 一詞指的是一個執行時元件,負責將收到的訊息轉換為使用者服務中的方法調用,並將該方法的返回值轉換為發出訊息。 WCF 服務會為每個端點建立 EndpointDispatcher。

EndpointDispatcher 會使用 EndpointDispatcher 類別,為端點範圍 (針對服務已接收或傳送的所有訊息) 提供擴充性。 這個類別可讓您自訂各種屬性,以控制 EndpointDispatcher 的行為。 此範例著重於 InstanceProvider 屬性,這個屬性會指向提供服務類別之執行個體的物件。

IInstanceProvider

在 WCF 中,EndpointDispatcher 藉由使用會實作 IInstanceProvider 介面的執行個體提供者,來建立服務類別的執行個體。 這個介面只有兩個方法:

物件池

ObjectPoolInstanceProvider 類別包含物件集區 (Object Pool) 的實作。 這個類別會實作 IInstanceProvider 介面,以便與服務模型層互動。 當 EndpointDispatcher 呼叫 GetInstance 方法而不是建立新執行個體時,自訂實作會在記憶體中集區尋找現有的物件。 如果有可用的,就會將其傳回。 否則,ObjectPoolInstanceProvider 會檢查 ActiveObjectsCount 屬性 (從集區傳回的物件數目) 是否已達到集區的大小上限。 如果不是這樣,則會建立新執行個體並將其傳回給呼叫者,接著 ActiveObjectsCount 就會遞增。 否則,物件建立請求會在配置的時間內被佇列處理。 下列範例程式碼會示範 GetObjectFromThePool 實作。

private object GetObjectFromThePool()
{
    bool didNotTimeout =
       availableCount.WaitOne(creationTimeout, true);
    if(didNotTimeout)
    {
         object obj = null;
         lock (poolLock)
        {
             if (pool.Count != 0)
             {
                   obj = pool.Pop();
                   activeObjectsCount++;
             }
             else if (pool.Count == 0)
             {
                   if (activeObjectsCount < maxPoolSize)
                   {
                        obj = CreateNewPoolObject();
                        activeObjectsCount++;

                        #if (DEBUG)
                        WritePoolMessage(
                             ResourceHelper.GetString("MsgNewObject"));
                       #endif
                   }
            }
           idleTimer.Stop();
      }
     // Call the Activate method if possible.
    if (obj is IObjectControl)
   {
         ((IObjectControl)obj).Activate();
   }
   return obj;
}
throw new TimeoutException(
ResourceHelper.GetString("ExObjectCreationTimeout"));
}

自訂 ReleaseInstance 實作會將釋放的實例返回至池,並遞減 ActiveObjectsCount 的值。 EndpointDispatcher 可以從不同的執行緒來呼叫這些方法,因此便需要在 ObjectPoolInstanceProvider 類別中同步存取類別層級的成員。

public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
    lock (poolLock)
    {
        // Check whether the object can be pooled.
        // Call the Deactivate method if possible.
        if (instance is IObjectControl)
        {
            IObjectControl objectControl = (IObjectControl)instance;
            objectControl.Deactivate();

            if (objectControl.CanBePooled)
            {
                pool.Push(instance);

                #if(DEBUG)
                WritePoolMessage(
                    ResourceHelper.GetString("MsgObjectPooled"));
                #endif
            }
            else
            {
                #if(DEBUG)
                WritePoolMessage(
                    ResourceHelper.GetString("MsgObjectWasNotPooled"));
                #endif
            }
        }
        else
        {
            pool.Push(instance);

            #if(DEBUG)
            WritePoolMessage(
                ResourceHelper.GetString("MsgObjectPooled"));
            #endif
        }

        activeObjectsCount--;

        if (activeObjectsCount == 0)
        {
            idleTimer.Start();
        }
    }

    availableCount.Release(1);
}

方法提供「初始化清理」功能。 集區通常會在集區的存留期中保留最低限度數目的物件。 不過,可能有些時段會出現過度使用的情形,因而需要在集區中建立其他物件以達到組態中指定的上限。 最後當集區的活動變少時,這些多餘的物件可能會變成額外的負荷。 因此,當 activeObjectsCount 成為零時,閒置計時器就會啟動,以觸發並執行清除循環。

if (activeObjectsCount == 0)
{
    idleTimer.Start();
}

您可以使用下列行為來連結 ServiceModel 層的延伸項目:

  • 服務行為:允許自訂整個服務執行階段。

  • 端點行為:允許自訂特定服務端點,包括 EndpointDispatcher。

  • 合約行為:允許分別在用戶端或服務上自訂 ClientRuntime 或 DispatchRuntime 類別。

  • 作業行為:允許分別在用戶端或服務上自訂 ClientOperation 或 DispatchOperation 類別。

為了實現物件池擴展,可以建立端點行為或服務行為。 這個範例採用服務行為,服務行為可以將物件共用能力套用至服務的每個端點。 服務行為是藉由實作 IServiceBehavior 介面來建立。 有一些方法可以讓 ServiceModel 察覺自訂行為:

  • 使用自訂屬性。

  • 以命令方式將它加入至服務描述的行為集合。

  • 延伸組態檔。

這個範例會使用自訂屬性。 建構 ServiceHost 時,會檢查服務型別定義中使用的屬性,並將可用的行為加入至服務描述的行為集合。

IServiceBehavior 介面具有三種方法:Validate,AddBindingParameters, 和 ApplyDispatchBehavior。 在初始化 WCF 時,ServiceHost 會呼叫這些方法。 首先會呼叫 IServiceBehavior.Validate,這個方法允許檢查服務有無不一致之處。 接著會呼叫 IServiceBehavior.AddBindingParameters,這個方法只有在非常進階的狀況中才需要使用。 最後會呼叫 IServiceBehavior.ApplyDispatchBehavior,這個方法負責設定執行階段。 下列參數會傳遞至 IServiceBehavior.ApplyDispatchBehavior:

  • Description:這個參數提供整個服務的服務描述, 可以用來檢查有關服務端點、合約、繫結程序以及與服務相關聯之其他資料的描述資料。

  • ServiceHostBase:這個參數提供目前正在初始化的 ServiceHostBase。

在自訂 IServiceBehavior 實作中,會為 ObjectPoolInstanceProvider 產生新的執行個體,並將新執行個體指派至附加到 InstanceProvider 之每個 EndpointDispatcher 中的 ServiceHostBase 屬性。

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    if (enabled)
    {
        // Create an instance of the ObjectPoolInstanceProvider.
        instanceProvider = new ObjectPoolInstanceProvider(description.ServiceType,
        maxPoolSize, minPoolSize, creationTimeout);

        // Assign our instance provider to Dispatch behavior in each
        // endpoint.
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
             ChannelDispatcher cd = cdb as ChannelDispatcher;
             if (cd != null)
             {
                 foreach (EndpointDispatcher ed in cd.Endpoints)
                 {
                        ed.DispatchRuntime.InstanceProvider = instanceProvider;
                 }
             }
         }
     }
}

除了 IServiceBehavior 實作之外,ObjectPoolingAttribute 類別還有數個可以使用屬性引數來自訂物件集區的成員。 這些成員包括 MaxSize、MinSize、Enabled 和 CreationTimeout,可用來比對 .NET Enterprise Services 提供的物件共用功能集。

現在,使用新建立的自訂 ObjectPooling 屬性對服務實作加上附註,即可將物件共用行為加入至 WCF 服務。

[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]
public class PoolService : IPoolService
{
  // …
}

攔截啟動與停用

物件共用的主要目標是,最佳化存留較短且建立和初始化時較會耗費資源的物件。 因此,好好利用的話,可以大幅提高應用程式的效能。 由於物件是從集區傳回,因此只能呼叫建構函式一次。 不過,有些應用程式會需要某些層級的控制權,以便初始化及清除單一內容期間使用的資源。 例如,用於一組計算的物件可以在處理下個計算之前,先重設私用欄位。 Enterprise Services 藉由讓物件開發者覆寫來自 Activate 基底類別的 Deactivate 和 ServicedComponent 方法,啟用了這種具體情境的初始化。

物件集區會在從集區傳回物件之前,呼叫 Activate 方法。 當物件傳回資源池時,會呼叫 Deactivate。 ServicedComponent 基底類別也有名為 boolean 的 CanBePooled 屬性,可以用來通知集區是否能進一步共用物件。

為了模擬此功能,範例會宣告具有上述成員的公用介面 (IObjectControl)。 接著,服務類別會實作這個介面,以提供內容特定的初始化。 IInstanceProvider 實作必須經過修改,才能符合這些需求。 現在,每次您透過呼叫 GetInstance 方法來取得物件時,就必須檢查物件是否實作 IObjectControl.。如果已實作,則必須適當地呼叫 Activate 方法。

if (obj is IObjectControl)
{
    ((IObjectControl)obj).Activate();
}

將物件傳回集區時,必須先檢查 CanBePooled 屬性,才能將物件新增回集區。

if (instance is IObjectControl)
{
    IObjectControl objectControl = (IObjectControl)instance;
    objectControl.Deactivate();
    if (objectControl.CanBePooled)
    {
       pool.Push(instance);
    }
}

由於服務開發人員可以決定是否可以共用物件,在特定時間內,集區中的物件計數可能會低於大小下限。 因此,您必須檢查物件計數是否曾經低於最低層級,並且在清除程序中執行必要的初始化。

// Remove the surplus objects.
if (pool.Count > minPoolSize)
{
  // Clean the surplus objects.
}
else if (pool.Count < minPoolSize)
{
  // Reinitialize the missing objects.
  while(pool.Count != minPoolSize)
  {
    pool.Push(CreateNewPoolObject());
  }
}

當您執行範例時,作業要求和回應會顯示在服務與用戶端主控台視窗中。 在每個主控台視窗中按下 Enter 鍵,即可關閉服務與用戶端。

若要安裝、建置及執行範例

  1. 確定您已執行 Windows Communication Foundation 範例的一次性安裝程序。

  2. 若要建置解決方案,請依照建置 Windows Communication Foundation 範例中的指示操作。

  3. 若要在單一或多部電腦組態中執行此範例,請遵循執行 Windows Communication Foundation 範例中的指示進行。