65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF 通道的正确使用和处置(或 CommunicationObjectFaultedException?!*#*$)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (12投票s)

2010 年 4 月 19 日

CPOL

1分钟阅读

viewsIcon

59769

在这篇文章中,我将探讨期望 'using' 块清理你的 WCF 通道所带来的微妙但灾难性的后果。

在这篇文章中,我将探讨期望 using 块清理你的 WCF 通道所带来的微妙但灾难性的后果。

注意:示例展示了生成代理的使用,但该问题和解决方案适用于所有 ICommunicationObject,包括生成的代理(ClientBase<T>)以及 ChannelFactoryChannelFactory<T>

许多不眠之夜都花在了思考为什么,无论进行何种异常处理,以下代码在 .HelloWorld() 抛出异常时都会抛出 CommunicationObjectFaultedException,即使该异常已被捕获。

典型的 'using' 处置模式

using (WCFServiceClient c = new WCFServiceClient())
{
    try
    {
        c.HelloWorld();
    }
    catch (Exception ex)
    {
        // handle the exception
    }
} 

请考虑,当在使用通道时发生异常时,它会进入 Faulted 状态,并且在这种 Faulted 状态下,调用 .Close() 将会抛出 CommunicationObjectFaultedException

如你所知,using 语句确保在退出 using 块之前调用 .Dispose()。对于通道,通常具有 private .Dispose() 方法,.Dispose() 只是简单地调用 .Close()。啊哈!你明白我的意思了吗?

典型的 using 处置模式陷阱说明

using (WCFServiceClient c = new WCFServiceClient())
{
    try
    {
        c.HelloWorld();
    }
    catch (Exception ex)
    {
        // You don't know it yet but your mellow has just been harshed.

        // If you handle this exception and fall through you will 
        // still be cheerfully greeted with an unhandled 
        // CommunicationObjectFaultedException when 'using' tries to .Close() the client.

        // If you throw or re-throw from here you will never see that exception, 
        // it is gone forever. 
        // buh bye.
        // All you will get is an unhandled CommunicationObjectFaultedException
    }
} // <-- here is where the CommunicationObjectFaultedException is thrown

解决此问题的办法是确保通道在关闭 using 块时能够成功过渡到 Closed 状态。这可以通过在你的 catch 块中调用 .Abort() 来确认 Faulted 状态,从而实际关闭通道,尽管方式比较突然。任何后续的 .Close() 调用都将无效。

正确的 using 处置模式

using (WCFServiceClient client = new WCFServiceClient())
{
    try
    {
        client.ThrowException();
        
    }
    catch (Exception ex)
    {
        // acknowledge the Faulted state and transition to Closed
        client.Abort();

        // handle the exception or rethrow, makes no nevermind to me, my
        // yob is done ;-D
    }
}

在某些情况下,你的周围代码的结构并不适合使用 using 块。

虽然 using 块具有其优点,例如块提供的作用域,但在通道的上下文中,它所做的只是调用 .Close(),而我们可以很容易地做到这一点。

不使用 using 的通道的正确使用

WCFServiceClient c = new WCFServiceClient();

try
{
    c.HelloWorld();
}
catch
{
    // acknowledge the Faulted state and transition to Closed
    c.Abort();

    // handle or throw
    throw;
}
finally
{
    c.Close();
}

这就是我对 WCF 通道的正确使用和处置的看法。

© . All rights reserved.