写点什么

WCF 的问题和 Using 语句块

  • 2009 年 3 月 11 日
  • 本文字数:1689 字

    阅读完需:约 6 分钟

WCF 客户端不能用在 Using 语句块中,因为它可能会抛出不可预知的异常。即使你捕获了异常,仍有可能一直保持连接。让我们来看看形成这一问题的历史原因,并提出几个补救措施。

在.NET 中,资源管理的基础就是 IDisposable 和 Using 语句块。除了 CLR 对象,.NET 中一切对象均使用这些工具进行管理。因此,我们需要知道为何微软对于 WCF 框架的资源管理如此一筹莫展。

WCF 客户端的首要问题是 Close/Dispose 方法会抛出异常。这与框架设计指南以及 IDisposable 规约背道而驰,从而导致 Dispose 方法可以在 Finally 语句块中被不安全的调用。

更糟糕的是,只要不调用 Abort,Close/Dispose 方法就会一直保持连接。太多的连接打开就会带来性能的问题,应用程序也会变得不够稳定。

在新闻组中,有关此问题的讨论可以追溯到 2006 年,Brian McNamara 介绍了这一设计缺陷的幕后故事

ICommunicationObject(它是 ServiceHost,ClientBase,IChannel,IChannelFactory 与 IChannelListener 最终继承的对象) 总是具有关闭对象的两个方法:(a)Close,(b)Abort。按照字面的理解,如果希望主动关闭对象,则调用 Close;若要强制关闭则调用 Abort。

因此,Close() 方法会接收一个 Timeout 参数,并包括一个异步版本(因为它可能阻塞线程),而且 Close() 还会抛出异常。Close 抛出的异常为 CommunicationException(CommunicationObjectFaultedException 是其子类)与 TimeoutException。

相反,Abort() 并不会阻塞线程(也不会抛出任何异常),因此没有 Timeout 值,也并不包含异步版本。

这两个概念从最初的 Indigo 一直沿用至今 [译注:所谓至今是指 Brian 发表帖子的时间 2006 年 10 月 25 日]。就目前而言一切正常。

最初的定义为 ICommunicationObject : IDisposalbe。作为一个标记接口,我们认为它可以用于通知用户在可能的时候即刻释放对象。然而问题却接踵而来。

从 Beta 1 版本开始,我们修改了 Dispose(),让其等同于 Abort() 方法。一部分原因是 Dispose() 应该完成最起码的必要的对象清理工作。在 Beta 1 中,这可能算得上是我们的头号麻烦了。用户可以将它们的通道(channel)对象放在 using() 语句块中,缓存中任何等待被取出的消息都可能会丢失。事务无法提交,会话可能得到告知收到(ACKed)的消息等。

鉴于用户的反馈,在 Beta 2 中我们又修改了实现,让 Dispose() 近似等于 Close()。我们知道,异常的抛出是问题之所在(部分原因在这篇帖子中已经说明),因此我们试图 让 Dispose 变得更加“聪明”。那就是说,如果当前并非 Opened 状态,就会在内部调用 Abort()。这仍然存在一系列问题,最主要的是你无法从 可靠性角度推断系统。Dispose 仍然会抛出异常,但并非总是会通知你某些事情发生错误。最终,我们决定将 IDisposable 从 ICommunicationObject 中移走。经过几番争辩,IDisposable 在 ServiceHost 和 ClientBase 中被保留了下 来,因为从理论上讲,对于多数用户而言 Dispose 抛出异常仍然是可以接受的,他们更偏向于使用 using() 的便利性,具有该标记接口就可以更及时地 清除对象。你可能主张(我们的一部分开发人员抱有同样的态度):应该将它从这两个类中移走,然而好也罢歹也罢,我们终究作出了选择。对于这个问题,你永远 都不可能达成一致,因此我们在 SDK 样例中给出了最佳实践,那就是遵循 try{Close}/catch{Abort}范式。

补救措施

Steve Smith 提出了 CloseConnection 扩展方法 [译注:原文并没有给出 CloseConnection 扩展方法的帖子,你可以访问 IDisposable 与 WCF ]。在 Finally 语句块中可以调用该方法,而不是调用 Close,它封装了 Close/Abort 逻辑。

新闻组的发帖人 bog1978 建议使用 C# lambda 以支持创建类似Using 的结构。方法接收一个新的客户端对象,以及一个匿名方法,该方法持有的代码与正常使用using 语句块包含的代码完全相同。

最后,还有一个补救措施是Erwyn Van Der Meer 定义的 WCF 服务代理辅助类。用户可以创建它,而不是通常的代理类,它可以纠正在关闭连接时出现的问题。一旦创建,它就会自动构建实际的代理,然后通过只读属性暴露它。

查看英文原文: The Problems with WCF and the Using Block

2009 年 3 月 11 日 04:172119
用户头像

发布了 109 篇内容, 共 37.0 次阅读, 收获喜欢 11 次。

关注

评论

发布
暂无评论
发现更多内容

架构实战营模块一作业

李焕之

模块一

Tim

架构实战营

常见内存泄漏引起原因

金陵老街

面试侃集合之SynchronousQueue非公平模式篇

码农参上

非公平锁 SynchronousQueue 8月日更

带你读AI论文丨LaneNet基于实体分割的端到端车道线检测

华为云开发者社区

端到端 网络模型 车道线 实体分割 LanNet

模块一作业

bin

如何在 MacOS 上降级 Java 版本

escray

学习 8月日更

模块1作业

atob

架构实战课程 模块一作业

Frank

0基础架构入门 - 1(架构概述)

felix

架构实战营 0基础架构入门

云小课|想实现资源全自动备份?看完这篇秘籍,不再蕉绿~

华为云开发者社区

云备份 自动备份 备份策略

深入了解现代web浏览器(第二部分)

GKNick

大前端 浏览器

模块一作业

Geek_85eb5f

Pulsar 周报 2021-08-09 ~ 2021-08-15

Apache Pulsar

Apache Pulsar StreamNative

架构实战训练营模块 1 作业

斯蒂芬.赵

架构实战营

微服务的痛:用实际经历告诉你它有多坑(三)

我爱娃哈哈😍

微服务 架构设计

新生代农民工的十八般武艺,你都了解吗

华为云开发者社区

DevOps 云原生 编程语言 编程规范 新生代农民工

架构实战营-模块一作业

Alex.Wu

linux笔记:极简方式安装mysql,建议收藏

小鲍侃java

8月日更

深层剖析鸿蒙轻内核M核的动态内存如何支持多段非连续性内存

华为云开发者社区

鸿蒙 内存 结构体 OpenHarmony 动态内容

架构实战☞ 微信业务架构&学生管理系统架构设计

眼镜盒子

架构实战营

故事篇:数据库架构演变之路

阿Q说代码

主从复制 读写分离 集群部署 8月日更 垂直拆分

一文为你介绍ServiceComb Service-Center三大高性能优化点

华为云开发者社区

开源 微服务 注册中心 Service-Center

微信朋友圈高性能复杂度分析

刘琦Logan

深入虚拟机探索Thread start

4ye

源码 线程 后端 JVM 8月日更

Vue进阶(七十二):css 样式中 逗号、空格、冒号、点号、~、>的区别

No Silver Bullet

Vue 8月日更

作业

Doctor Blind

架构实战营

TP6+layui2.6.8开发的管理系统(FunAdmin),内置Curd命令模式 在线更新

funadmin

CMS layui 商城 管理系统 funadmin

apipost使用脚本发送一个接口请求

与风逐梦

后端 开发工具 接口测试

Go- map的定义

HelloBug

Go 语言 map的定义

拆分电商系统为微服务

tjudream

微服务 电商 拆分

WCF的问题和Using语句块-InfoQ