WCF中使用"using"语句时出现异常,无法正确关闭连接。如何关闭故障的WCF客户端连接或那些出现异常的连接?

5

在StackOverflow上有几个关于关闭WCF连接的问题,但是排名最高的答案都指向了这篇博客:

http://marcgravell.blogspot.com/2008/11/dontdontuse-using.html

当我在服务器上设置断点并让客户端挂起超过一分钟时(我有意制造一个超时异常),我发现这种技术存在问题。

问题在于,客户端似乎会“挂起”,直到服务器完成处理。我猜测所有内容都是在异常后清理的。

关于TimeOutException,看起来客户端的retry()逻辑将继续不断地向服务器重新提交查询,而我可以看到服务器端的调试器将请求排队然后同时执行每个排队的请求。我的代码没有预料到WCF会以这种方式运作,这可能是我看到数据损坏问题的原因。

这个解决方案似乎有些问题。

在WCF代理中,处理故障和异常的全面现代方法是什么?

2个回答

7

更新

诚然,这是一段有点枯燥的代码。我目前更喜欢这个链接的答案,并且没有看到其中任何可能会在未来引起问题的“黑科技”。


这是微软处理WCF客户端调用的推荐方式:
更多细节请参见:预期异常
try
{
    ...
    double result = client.Add(value1, value2);
    ...
    client.Close();
}
catch (TimeoutException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}
catch (CommunicationException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}

附加信息 许多人似乎在WCF上问这个问题,微软甚至创建了一个专门的示例来演示如何处理异常:

c:\WF_WCF_Samples\WCF\Basic\Client\ExpectedExceptions\CS\client

下载示例:C#VB

考虑到 使用语句(激烈的?)内部讨论线程 上存在很多问题,我不打算浪费时间尝试成为一个代码牛仔并找到更简洁的方法。对于我的服务器应用程序,我只会咬紧牙关,采用这种冗长但可信的方式实现 WCF 客户端。 可选的其他故障捕获 许多异常都源自 CommunicationException,我认为大多数这些异常都不应该重试。我在 MSDN 上钻研了每个异常,并列出了一份可以重试的异常短列表(除了上面提到的 TimeOutException)。如果我漏掉了一个需要重试的异常,请告诉我。
Exception   mostRecentEx = null;
for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
{
    try
    {
       ...
       double result = client.Add(value1, value2);
       ...
       client.Close();
    }

    // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
    catch (ChannelTerminatedException cte)
    {

      mostRecentEx = cte;
      secureSecretService.Abort();
        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
    // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
    catch (EndpointNotFoundException enfe)
    {

      mostRecentEx = enfe;
     secureSecretService.Abort();
        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    // The following exception that is thrown when a server is too busy to accept a message.
    catch (ServerTooBusyException stbe)
    {
      mostRecentEx = stbe;
        secureSecretService.Abort();

        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    catch(Exception ex)
    { 
         throw ex;  // rethrow any other exception not defined here
    }
}
if (mostRecentEx != null) 
{
    throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
}

3

关闭和释放WCF服务

正如该帖子所提到的,当没有异常时应该使用Close方法,当有错误时应该使用Abort方法。不应该使用Dispose和Using方法来释放WCF。


1
根据该问题中的链接,我认为WCF应该没有实现IDisposable,而是应该有一个完全不同的清理架构。 - NotMe
@Chris Lively 确实,似乎每个人都试图拼凑出一个using语句,这违背了微软的建议。微软内部的讨论显然希望生产应用程序具有特定的结构化异常处理。这将是冗长而烦人的,但我想我需要接受它。 - makerofthings7
我目前无法找到链接,但我看到一个网站上有人以相当有趣的方式使用lambda来掩盖这种行为。 这使得代码更易于阅读。 如果我再次遇到它,我会贴出链接。 - Avilo
这不是我想要的页面,但这是一个很好的模板类,可以帮助解决问题。 它还链接到MSDN论坛,其中有一个MSFT开发人员谈论它。 - Avilo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接