Webアプリから使用しているWCFサービスがあります。使用しているクライアントは、VisualStudioの[サービス参照の追加]オプションを使用して生成されました。これはWebアプリであり、アプリの性質上、セッションが比較的短くなる可能性があるため、ユーザーがログインしたときにクライアントのインスタンスを作成し、セッションの間保持することを選択しました。セッションが終了したら、それを破棄します。
これは私の質問に私をもたらします-私たちは障害状態に入るクライアントのチャネルを処理するための最良の方法を決定しようとしています。いくつかを検索した後、私たちはこれを思いついた:
if(client.State = CommuncationState.Faulted)
{
client = new Client();
}
try
{
client.SomeMethod();
}
catch //specific exceptions left out for brevity
{
//logging or whatever we decide to do
throw;
}
ただし、少なくともこの場合、サービスがダウンしていても、実際にそれを使用して電話をかけようとするまで、クライアントはOpen
状態を表示するため、これは機能しません。次に、それがFaulted
状態に入るポイント。
だから、これは私たちに何か他のことをすることを任せます。私たちが思いついた別のオプションは次のとおりです。
try
{
client.SomeMethod();
}
catch
{
if(client.State == CommunicationState.Faulted)
{
//we know we're faulted, try it again
client = new Client();
try
{
client.SomeMethod();
}
catch
{
throw;
}
}
//handle other exceptions
}
しかし、それはにおいがします。明らかに、新しいクライアントを使用し、呼び出しごとにそれを破棄することで、これを回避できます。それは不必要に思えますが、それが正しい方法であれば、それが私たちが選ぶものだと思います。では、クライアントが障害状態にあるかどうかを判断し、それに対して何かを実行するための最善の方法は何でしょうか。本当にすべての呼び出しに対して新しいクライアントを取得する必要がありますか?
もう1つ覚えておくべきことは、クライアントのインスタンス化とこのすべてのチェックと処理は、クライアントのラッパークラスで行われます。これを意図した方法で行うと、アプリ自体に対して透過的になります。呼び出しを行ったり、それらから例外を処理したりするために、特別なコードは必要ありません。
質問に答えるには、ChannelFactoryプロパティの Faulted event を次のように処理できます。
client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
これにより、必要なロギング/クリーンアップを実行できるようになります。
一般的な推奨事項として、セッション中はチャネルを開いたままにしないでください。終了したら、チャネルを適切に閉じていることを確認してください(例外的に中止します)。
また、可能であれば、Visual Studio Add Service Referenceを使用しないこと、または少なくとも生成されたコード/構成をクリーンアップすることを検討してください。プロキシ実装を使用する場合は、 ClientBase から派生して独自の実装を作成するか、 ChannelFactory 実装を使用することをお勧めします。ラッパークラスについて言及しているので、ChannelFactoryを使用して、クリーンアップのニーズに合わせて Faulted event を処理することをお勧めします。
クライアントプロキシで.Faultedイベントを処理してみてください。例:
_((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);
private void client_Faulted(object sender, EventArgs e)
{
client = new Client();
((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);
}
_
チャネルに障害が発生した瞬間にトリガーされ、チャネルを再度開く機会が与えられます。
また、client
メソッドへの各呼び出しをtry-catchブロックでラップする必要があります。また、呼び出しをn回再試行して失敗をログに記録するwhile()
ループでラップすることもできます。例えば:
_bool succeeded = false;
int triesLeft = 3;
while (!succeeded && triesLeft > 0)
{
triesLeft--;
try
{
client.SomeMethod();
succeeded = true;
}
catch (exception ex)
{
logger.Warn("Client call failed, retries left: " + triesLeft;
}
}
if (!succeeded)
logger.Error("Could not call web service");
_
私のコードでは、ManualResetEvent
を使用してwhile()ループをブロックし、_client_Faulted
_イベントハンドラーがclient
を再作成する機会を得ました。プロキシ。