IErrorHandler と IServiceBehavior を実装するクラスを作成しました。この動作を ServiceHost に追加して実行します。
私が IErrorHandler について学ぼうとした理由は、コールバック チャネルとやり取りするすべてのサービス コードを try catch ステートメントでラップする必要があり、例外をキャッチするグローバルな方法があるかどうか疑問に思ったからです。
ここ StackOverflow で読んだことから、それがまさに私が欲しいものであることがわかりました。
しかし、実行中、それは私を大いに混乱させます。次のことを行うコントラクトメソッドがある場合...
Dictionary<IChatCallback, string> userChannels = new Dictionary<IChatCallback, string>();
public void SendMessage(string message)
{
IChatCallback callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
string senderName = userChannels[callback];
SendMessageToAllUsers(message, senderName);
}
public void SendMessageToAllUsers(string message, string sender)
{
foreach (var userChannel in userChannels.Keys)
{
userChannel.ReceiveMessage(new ChatMessage(message, sender));
}
}
クライアントが私のサーバーにいて、彼が地球の表面から落ちてチャネルに障害が発生した場合、サーバーがチャネルに障害があることに気付くまでにしばらく時間がかかるようです. ICommunicationObject のイベントにサブスクライブしていても、サーバーがチャネルに障害があることを認識するまでに時間がかかるようです。たとえば、誰かがメッセージを送信していて、誰かが切断し、同時にメッセージが伝播されているとします。
さて、基本的に定期的にクライアントに ping を送信して、クライアントがまだ存在するかどうかを確認する別の方法があるので、クライアントをuserChannel
辞書から削除できます。
ここで IErrorHandler はどのくらい正確に私を助けてくれるのでしょうか? エラーをキャッチしたとしても、サービスがクラッシュするのを正確に助けているわけではないようです。私のサービスはクライアントに非常に速いペースで ping を送信できないため、例外をスローした前のメッセージの直後に (私はこれをテストしていました)、メッセージが届くことがあります。最初のものは処理されたように見えましたが、2 番目のものはキャッチされなかった別の例外をスローし、サーバーが障害のあるチャネルと通信しようとしたため、サービスがクラッシュしました。現在、私のサービスは障害のあるチャネルをクリーンアップしますが、定期的にクリーンアップします。
IErrorHandler を実装することで、すべてのメソッドを try{}catch{} ブロックでラップする必要がなくなることを期待していましたが、障害のあるチャネルと通信しているかどうかを確認するために、まだ必要なようです。
あるいは、IErrorHandler を不適切に使用して、本来すべきではないことを行うべきだと考えているだけかもしれません。サービスにそれを実装させ、それ自体を IServiceBehavior として Servicehost に挿入する必要がありますか? そして、HandleError() メソッドで、クライアントのリストからチャネルを削除しますか? サービス クラスに WCF 配管要素を配置しているため、これはややこしいように思えますが、例外コード自体にコールバックのリストからチャネルを削除させるには、これしか考えられません。
私はそれが初めてなので、たくさんの読書をしようとしていますが、WCF のエラー処理の世界は気が遠くなるような気がします。
Faulted イベントをサブスクライブしてサービスで呼び出されないようにチャネルを削除しようとしても、十分な速度で動作せず、IErrorHandler を使用してもサーバーが回復できないように見える例外がスローされます。
たとえば、ユーザーが最初に接続したときに、イベントをサブスクライブします。
IChatCallback callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
ICommunicationObject callbackComm = (ICommunicationObject) callback;
callbackComm.Faulted += (sender, e) =>
{
lock (lockObject)
{
string name = userChannels[callback];
userChannels.Remove(callback);
NotifyOfUserDisconnect(name);
}
};
すべてのメソッドにアクセスして、チャネルが閉じているかエラーが発生しているかを確認し、すべてを try/catch ブロックでラップして参照を削除し、クライアントに通知する必要がありますか?