カスタム有効期間
Lifetime のサンプルでは、Windows Communication Foundation (WCF) 拡張を記述して、共有 WCF サービス インスタンスにカスタムの有効期間サービスを提供する方法を示します。
Note
このサンプルのセットアップ手順とビルド手順については、この記事の最後を参照してください。
共有インスタンス化
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 が PerCall または PerSession に構成されているかどうかを確認し、そうである場合は、インスタンスを解放してリソースを要求します。 カスタムの IInstanceContextProvider が使用されている場合、WCF では、インスタンスを解放する前に、プロバイダーの実装の IsIdle メソッドを呼び出します。 IsIdle から true
が返された場合、インスタンスは解放されます。それ以外の場合、IInstanceContextProvider 実装では、コールバック メソッドを使用してアイドル状態であることを Dispatcher
に通知します。 これは、プロバイダーの NotifyIdle メソッドを呼び出すことで行われます。
このサンプルでは、アイドル タイムアウトが 20 秒である InstanceContext の解放を遅延させる方法を示します。
InstanceContext の拡張
WCF の InstanceContext は、サービス インスタンスと Dispatcher
の間のリンクです。 WCF では、拡張可能オブジェクト パターンを使用して新しい状態または動作を追加することで、このランタイム コンポーネントを拡張できます。 拡張可能オブジェクト パターンは、既存のランタイム クラスに新しい機能を付け加えて拡張するため、またはオブジェクトに新しい状態の機能を追加するために WCF で使用されます。 拡張可能オブジェクト パターンには、IExtensibleObject<T>、IExtension<T>、および IExtensionCollection<T> の 3 つのインターフェイスがあります。
IExtensibleObject<T> インターフェイスは、機能をカスタマイズするための拡張が可能なオブジェクトによって実装されます。
IExtension<T> インターフェイスは、T
型のクラスの拡張が可能なオブジェクトによって実装されます。
最後に、IExtensionCollection<T> インターフェイスは、その型によって IExtension<T> の実装を取得できるようにする IExtension<T> 実装のコレクションです。
したがって、InstanceContext を拡張するには、IExtension<T> インターフェイスを実装する必要があります。 このサンプル プロジェクトでは、CustomLeaseExtension
クラスにこの実装が含まれています。
class CustomLeaseExtension : IExtension<InstanceContext>
{
}
IExtension<T> インターフェイスには、Attach と Detach の 2 つのメソッドが含まれています。 名前が示すように、これらの 2 つのメソッドは、ランタイムが 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 メソッドを呼び出すと、この呼び出しは CustomLeaseExtension
の IsIdle メソッドにルーティングされます。 次に、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 にルーティングするために CustomLeaseExtension
を実装します。 IInstanceContextProvider 実装は、CustomLifetimeLease
クラスに含まれています。 IsIdle メソッドは、WCF でサービス インスタンスを解放するときに呼び出されます。 ただし、ServiceBehavior の IInstanceContextProvider コレクションには、特定の ISharedSessionInstance
インスタンスは 1 つしか実装されていません。 つまり、WCF で InstanceContext メソッドがチェックされるときに、IsIdle が閉じられているかどうかを知る方法はありません。 したがって、このサンプルでは、スレッド ロックを使用して IsIdle メソッドへの要求をシリアル化します。
重要
シリアル化はアプリケーションのパフォーマンスに大きな影響を及ぼす可能性があるので、スレッド ロックは使用しないことをお勧めします。
プライベート メンバー フィールドは、アイドル状態を追跡するために CustomLifetimeLease
クラスで使用され、IsIdle メソッドによって返されます。 IsIdle メソッドが呼び出されるたびに、isIdle
フィールドが返され、false
にリセットされます。 ディスパッチャーが false
メソッドを呼び出すようにするには、この値を NotifyIdle に設定する必要があります。
public bool IsIdle(InstanceContext instanceContext)
{
get
{
lock (thisLock)
{
//...
bool idleCopy = isIdle;
isIdle = false;
return idleCopy;
}
}
}
IInstanceContextProvider.IsIdle メソッドから false
が返された場合、ディスパッチャーでは 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
プロパティをチェックする前に、Callback プロパティを設定する必要があります。これは、アイドル状態になったときに CustomLeaseExtension
からディスパッチャーに通知するために不可欠です。 ICustomLease.IsIdle
が true
を返す場合は、isIdle
プライベート メンバーが CustomLifetimeLease
で単に true
に設定され、コールバック メソッドが呼び出されます。 このコードでロックが保持されているので、他のスレッドではこのプライベート メンバーの値を変更できません。 次にディスパッチャーで IInstanceContextProvider.IsIdle をチェックするときに、true
が返され、ディスパッチャーでインスタンスを解放できるようになります。
これでカスタム拡張機能の基礎が完成したので、この拡張機能をサービス モデルにフックする必要があります。 CustomLeaseExtension
実装を InstanceContext にフックするために、WCF には、InstanceContext のブートストラップを実行する IInstanceContextInitializer インターフェイスが用意されています。 このサンプルでは、CustomLeaseInitializer
クラスでこのインターフェイスを実装し、CustomLeaseExtension
のインスタンスを唯一のメソッドの初期化から得られる Extensions コレクションに追加します。 このメソッドは、InstanceContext の初期化中にディスパッチャーによって呼び出されます。
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 キーを押すと、サービスとクライアントがどちらもシャットダウンされます。
サンプルをセットアップ、ビルド、および実行するには
Windows Communication Foundation サンプルの 1 回限りのセットアップの手順を実行したことを確認します。
ソリューションの C# 版または Visual Basic .NET 版をビルドするには、「 Building the Windows Communication Foundation Samples」の手順に従います。
単一または複数コンピューター構成でサンプルを実行するには、「Windows Communication Foundation サンプルの実行」の手順に従います。