共用方式為


自訂存留期

存留期範例示範如何撰寫 Windows Communication Foundation (WCF) 延伸模組,以便針對共用的 WCF 服務執行個體提供自訂存留期服務。

注意

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

共用執行個體

WCF 針對您的服務執行個體提供數種執行個體模式。 本文中涵蓋的共用執行個體模式提供一種方式,可以在多個通道間共用服務執行個體。 用戶端可以連絡服務中的處理站方法,並建立新的通道以開始通訊。 下列程式碼片段示範用戶端應用程式如何針對現有的服務執行個體建立新的通道:

// Create a header for the shared instance id
MessageHeader shareableInstanceContextHeader = MessageHeader.CreateHeader(
        CustomHeader.HeaderName,
        CustomHeader.HeaderNamespace,
        Guid.NewGuid().ToString());

// Create the channel factory
ChannelFactory<IEchoService> channelFactory =
    new ChannelFactory<IEchoService>("echoservice");

// Create the first channel
IEchoService proxy = channelFactory.CreateChannel();

// Call an operation to create shared service instance
using (new OperationContextScope((IClientChannel)proxy))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy.Echo("Apple"));
}

((IChannel)proxy).Close();

// Create the second channel
IEchoService proxy2 = channelFactory.CreateChannel();

// Call an operation using the same header that will reuse the shared service instance
using (new OperationContextScope((IClientChannel)proxy2))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy2.Echo("Apple"));
}

共用執行個體模式不像其他執行個體模式,前者擁有釋出服務執行個體的唯一方式。 根據預設,當 InstanceContext 的所有通道都關閉時,WCF 服務執行階段會檢查服務 InstanceContextMode 是否已設定為 PerCallPerSession,如果是的話,則會釋放執行個體並宣告資源。 如果使用自訂 IInstanceContextProvider,WCF 會在釋放執行個體之前叫用提供者實作的 IsIdle 方法。 如果 IsIdle 傳回 true,則執行個體已釋放,否則 IInstanceContextProvider 實作要負責使用回呼方法,通知 Dispatcher 閒置狀態。 這是藉由呼叫提供者的 NotifyIdle 方法來完成。

此範例示範如何延遲釋放 InstanceContext,閒置逾時為 20 秒。

擴充 InstanceContext

在 WCF 中,InstanceContext 是服務執行個體與 Dispatcher 之間的連結。 WCF 可讓您使用其可延伸物件模式加入新狀態或行為,以擴充這個執行階段元件。 在 WCF 中使用可擴充物件模式,可以搭配新功能來擴充現有執行階段類別,或新增狀態功能到物件。 在可延伸物件模式中有三種介面:IExtensibleObject<T>IExtension<T>IExtensionCollection<T>

IExtensibleObject<T> 介面是由允許自訂其功能的延伸模組所實作的。

IExtension<T> 介面是由可以是型別 T 之類別延伸模組的物件所實作。

最後,IExtensionCollection<T> 介面則是 IExtension<T> 實作的集合,可允許依其型別擷取 IExtension<T> 的實作。

因此,若要擴充 InstanceContext,您必須實作 IExtension<T> 介面。 在此範例專案中,CustomLeaseExtension 類別包含這個實作。

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

IExtension<T> 介面擁有兩種方法:AttachDetach。 顧名思義,當執行階段將延伸模組連接到 InstanceContext 類別的執行個體以及從其中斷時,會呼叫這兩個方法。 在此範例中,Attach 方法用於追蹤屬於目前延伸模組執行個體的 InstanceContext 物件。

InstanceContext owner;

public void Attach(InstanceContext owner)
{
    this.owner = owner;
}

此外,您必須將必要的實作加入至延伸模組,以提供延伸的存留時間支援。 因此,系統會使用所需的方法宣告 ICustomLease 介面,並在 CustomLeaseExtension 類別中實作。

interface ICustomLease
{
    bool IsIdle { get; }
    InstanceContextIdleCallback Callback { get; set; }
}

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

當 WCF 在 IInstanceContextProvider 實作中叫用 IsIdle 方法時,這個呼叫會路由至 CustomLeaseExtensionIsIdle 方法。 然後,CustomLeaseExtension 會檢查其私用狀態以查看 InstanceContext 是否閒置。 如果閒置,則會傳回 true。 否則,它會啟動計時器一段指定的延伸存留時間。

public bool IsIdle
{
  get
  {
    lock (thisLock)
    {
      if (isIdle)
      {
        return true;
      }
      else
      {
        StartTimer();
        return false;
      }
    }
  }
}

在計時器的 Elapsed 事件中,系統會呼叫 Dispatcher 中的回呼函式,以啟動另一個清除循環。

void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
    lock (thisLock)
    {
        StopTimer();
        isIdle = true;
        Utility.WriteMessageToConsole(
            ResourceHelper.GetString("MsgLeaseExpired"));
        callback(owner);
    }
}

當移到閒置狀態的執行個體有新訊息時,無法更新執行中的計時器。

此範例會實作 IInstanceContextProvider 以攔截對 IsIdle 方法的呼叫,並將其路由至 CustomLeaseExtensionIInstanceContextProvider 實作包含在 CustomLifetimeLease 類別中。 當 WCF 即將釋出服務執行個體時,會叫用 IsIdle 方法。 不過,在 ServiceBehavior 的 IInstanceContextProvider 集合中,只有一個特定 ISharedSessionInstance 實作的執行個體。 也就是說,在 WCF 檢查 IsIdle 方法時,沒有任何方式可以知道 InstanceContext 即將關閉。 因此,此範例使用執行緒封鎖來序列化 IsIdle 方法的要求。

重要

使用執行緒封鎖並不是建議的方法,因為序列化可能會嚴重影響應用程式的效能。

私人成員欄位在 CustomLifetimeLease 類別中用於追蹤閒置狀態,並由 IsIdle 方法傳回。 每次呼叫 IsIdle 方法時,都會傳回 isIdle 欄位,並重設為 false。 請務必將此值設定為 false,才能確保 Dispatcher 呼叫 NotifyIdle 方法。

public bool IsIdle(InstanceContext instanceContext)
{
    get
    {
        lock (thisLock)
        {
            //...
            bool idleCopy = isIdle;
            isIdle = false;
            return idleCopy;
        }
    }
}

如果 IInstanceContextProvider.IsIdle 方法傳回 false,Dispatcher 會使用 NotifyIdle 方法登錄一個回呼函式。 此方法會接收要釋出之 InstanceContext 的參考。 因此,範例程式碼可以查詢 ICustomLease 型別擴充,然後在擴充的狀態下檢查 ICustomLease.IsIdle 屬性。

public void NotifyIdle(InstanceContextIdleCallback callback,
            InstanceContext instanceContext)
{
    lock (thisLock)
    {
       ICustomLease customLease =
           instanceContext.Extensions.Find<ICustomLease>();
       customLease.Callback = callback;
       isIdle = customLease.IsIdle;
       if (isIdle)
       {
             callback(instanceContext);
       }
    }
}

ICustomLease.IsIdle 屬性進行檢查之前,必須設定回呼屬性,因為這在 CustomLeaseExtension 通知 Dispatcher 其變成閒置時相當重要。 如果 ICustomLease.IsIdle 傳回 trueisIdle 私用成員只會在 CustomLifetimeLease 中設定為 true,並呼叫回呼方法。 由於程式碼保留鎖定,因此其他執行緒無法變更此私用成員的值。 此外,在 Dispatcher 下次呼叫 IInstanceContextProvider.IsIdle 時,會傳回 true,然後讓 Dispatcher 釋出執行個體。

既然自訂延伸模組的基礎已完成,現在必須將其連結至服務模型。 為了將 CustomLeaseExtension 實作連結到 InstanceContext,WCF 會提供 IInstanceContextInitializer 介面來執行 InstanceContext 的啟動載入。 在此範例中,CustomLeaseInitializer 類別會實作此介面,並將 CustomLeaseExtension 的執行個體從唯一的方法初始化加入至 Extensions 集合。 初始化 InstanceContext 時,Dispatcher 會呼叫這個方法。

public void InitializeInstanceContext(InstanceContext instanceContext,
    System.ServiceModel.Channels.Message message, IContextChannel channel)

    //...

    IExtension<InstanceContext> customLeaseExtension =
        new CustomLeaseExtension(timeout, headerId);
    instanceContext.Extensions.Add(customLeaseExtension);
}

最後,使用 IServiceBehavior 實作,將 IInstanceContextProvider 實作連結至服務模型。 此實作放在 CustomLeaseTimeAttribute 類別中,而且它也衍生自 Attribute 基底類別,以便當做屬性公開此行為。

public void ApplyDispatchBehavior(ServiceDescription description,
           ServiceHostBase serviceHostBase)
{
    CustomLifetimeLease customLease = new CustomLifetimeLease(timeout);

    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceContextProvider = customLease;
            }
        }
    }
}

您可以使用 CustomLeaseTime 屬性加上註解,以便將此行為加入至範例服務類別。

[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
  //…
}

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

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

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

  2. 若要建置方案的 C# 或 Visual Basic .NET 版本,請遵循 Building the Windows Communication Foundation Samples中的指示。

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