处理持久性WCF客户端进入故障状态

时间:2011-03-28 19:41:56

标签: c# wcf exception-handling channel faulted

我们有一个WCF服务,我们正在通过网络应用程序消费。我们使用的客户端是使用Visual Studio“添加服务引用”选项生成的。由于它是一个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
}

但那闻起来。显然,我们可以通过使用新客户端并为每次调用处理它来避免这种情况。这似乎是不必要的,但如果这是正确的方式,那么我猜这是我们会选择的。那么,优雅地处理确定客户端是否处于故障状态然后对其进行某些操作的最佳方法是什么?我们真的应该为每次通话获得一个新客户吗?

要记住的另一件事 - 客户端的实例化以及所有这些检查和处理都发生在客户端的包装类中。如果我们以我们想要的方式执行此操作,它对应用程序本身是透明的 - 从中​​调用和处理异常不需要特殊代码。

2 个答案:

答案 0 :(得分:20)

要回答您的问题,您可以像这样处理ChannelFactory属性的Faulted event

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

这应该允许您执行您需要执行的任何记录/清理。

作为一般建议,您不应该在会话期间保持通道处于打开状态,因此请确保在完成通道后正确关闭通道(在例外情况下中止)。

此外,如果可能,请考虑不使用Visual Studio添加服务引用,或者至少清理它生成的代码/配置。我建议如果您想使用代理实现,请通过派生ClientBase创建自己的实现,或使用ChannelFactory实现。由于您提到了一个包装类,我建议您使用ChannelFactory并处理Faulted event以满足您的清理需求。

答案 1 :(得分: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);
}

它应该触发通道故障的瞬间,让你有机会重新打开它。

你还应该在try-catch块中将每个调用包装到client方法中,甚至可能将它包装在重试调用n次的while()循环中,然后记录失败。 EG:

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事件处理程序有机会重新创建{{1}代理。