有効期間のサンプルでは、共有 WCF サービス インスタンスのカスタム有効期間サービスを提供する Windows Communication Foundation (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 が 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 つのメソッドがあります。 これらの名前が示すように、ランタイムが拡張機能をアタッチし、 InstanceContext クラスのインスタンスにデタッチすると、これら 2 つのメソッドが呼び出されます。 このサンプルでは、 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 がIsIdle実装でIInstanceContextProvider メソッドを呼び出すと、この呼び出しはIsIdleのCustomLeaseExtension メソッドにルーティングされます。 次に、 CustomLeaseExtension はプライベート状態をチェックして、 InstanceContext がアイドル状態かどうかを確認します。 待機状態の場合は、trueを返します。 それ以外の場合は、延長された指定の有効期間のタイマーが開始されます。
public bool IsIdle
{
get
{
lock (thisLock)
{
if (isIdle)
{
return true;
}
else
{
StartTimer();
return false;
}
}
}
}
タイマーの Elapsed イベントでは、ディスパッチャーのコールバック関数が呼び出され、別のクリーンアップ サイクルが開始されます。
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 のISharedSessionInstance コレクションには、特定のIInstanceContextProvider実装のインスタンスが 1 つだけ存在します。 つまり、WCF が InstanceContext メソッドをチェックするときにIsIdleが閉じているかどうかを知る方法はありません。 そのため、このサンプルではスレッド ロックを使用して、 IsIdle メソッドに要求をシリアル化します。
Von Bedeutung
シリアル化はアプリケーションのパフォーマンスに重大な影響を与える可能性があるため、スレッド ロックの使用はお勧めできません。
プライベート メンバー フィールドは、アイドル状態を追跡するために 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 に設定され、コールバック メソッドが呼び出されます。 コードはロックを保持しているため、他のスレッドはこのプライベート メンバーの値を変更できません。 次に 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);
}
最後に、 IInstanceContextProvider 実装は、 IServiceBehavior 実装を使用してサービス モデルにフックされます。 この実装は 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 サンプルのOne-Time セットアップ手順を実行していることを確認します。
ソリューションの C# または Visual Basic .NET エディションをビルドするには、「Windows Communication Foundation サンプルのビルド」の手順に従います。
単一または複数のコンピューター間の構成でサンプルを実行するには、「Windows Communication Foundation Samplesの実行」の手順に従います。