19

Web アプリから使用している WCF サービスがあります。使用しているクライアントは、Visual Studio の [サービス参照の追加] オプションを使用して生成されました。これは Web アプリであり、アプリの性質上、セッションは比較的短いものになる可能性が高いため、ユーザーがログインしたときにクライアントのインスタンスを作成し、セッションが終了するまでそれを保持することにしました。セッションが終了したときにそれを破棄します。

これは私の質問につながります.Faulted状態に入るクライアントのチャネルを処理する最善の方法を決定しようとしています. いくつかを検索した後、これを思いつきました:

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 つ注意してください。クライアントのインスタンス化と、このすべてのチェックと処理は、クライアントのラッパー クラスで行われます。意図したとおりにこれを行うと、アプリ自体に対して透過的になります。呼び出しを行い、それらからの例外を処理するために特別なコードは必要ありません。

4

2 に答える 2

20

あなたの質問に答えるために、ChannelFactory プロパティのFaulted イベントを次のように処理できます。

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);

これにより、必要なロギング/クリーンアップを実行できるようになります。

一般的な推奨事項として、セッション中はチャネルを開いたままにしないでください。そのため、終了後にチャネルを適切に閉じている (例外が発生した場合に中止する) ことを確認してください。

また、可能であれば、Visual Studio の Add Service Reference を使用しないことを検討するか、少なくともそれが生成するコード/構成をクリーンアップすることを検討してください。プロキシ実装を使用する場合は、 ClientBaseから派生させて独自に作成するか、ChannelFactory実装を使用することをお勧めします。ラッパー クラスについて言及しているため、ChannelFactory を使用して、クリーンアップのニーズに合わせてFaulted イベントを処理することをお勧めします。

于 2011-03-28T20:24:47.473 に答える
13

クライアント プロキシで .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);
}

チャネルに障害が発生した瞬間にトリガーされ、チャネルを再開する機会が与えられます。

また、メソッドへの各呼び出しをclienttry-catch ブロックでラップする必要があります。おそらくwhile()、呼び出しを n 回再試行して失敗をログに記録するループでラップすることもできます。例えば:

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() ループをブロックするために a を使用するところまで行きました。client_Faultedclient

于 2011-03-28T20:15:56.307 に答える