坦白地说,Visual Studio 默认生成的 WCF 客户端有问题。它不能直接匹配服务器端接口代码,这导致了许多代码和数据共享问题,而且经过了这么多年,生成的代码仍然有一个明显的 Bug。在题为《更智能的WCF 服务客户端》的系列文章中, Michael Taylor 讨论了这些问题以及他为解决这些问题所做的努力。
关于 WCF 客户端代理,一个众所周知的问题是,只有在服务器真正运行的时候才能生成。对于许多团队来说,这使得在构建时更新服务引用即使可能也非常困难。可是,团队不得不依靠记忆将变化通知对方,并根据需要手动更新服务引用。Michael 继续写道:
用于生成引用的 URL 存储在生成的代码中(.svcmap)。如果引用被更新,就会使用原始 URL。而如果原始 URL 过时了或者引用了开发人员的机器,这就会产生问题。更糟糕的是,即使没有更改实际的代码,为了重新生成引用而更改 URL 也会导致所有的文件发生变化。
在使用服务引用样式的 WCF 客户端时,一个常见错误是使用“using”块。虽然常见,但这种做法并不合适,因为当调用 Dispose 时,WCF 客户端可能会抛出异常。这就是所谓的“ WCF Dispose 问题”,而变通做法很多。
Michael 还列举了其它问题,包括:
- 虽然 WCF 使用接口来隐藏实现细节,但生成的代码实际上包含了一个不同的接口,而这个接口恰巧共享了相同的名字。该接口在服务引用代码中定义。这使得跨项目使用原始接口更加困难。
- 更糟糕的是,部分方法签名可能会变化。比如,枚举和集合会默认转换成数组。甚至参数都可以移动。
- 在 Visual Studio 中,开发人员可以使用“查找所有引用(Find All References)”来找出对所有类型和成员的引用。但如果使用服务引用,那么 FAR 就无法检测到它们,这还是因为服务引用生成了新接口。
- 此外,任何已经订立的数据契约都可以修改,包括不属于原始对象的属性。如果任何代码使用了这些属性,而现在正在使用 WCF 提供的基础设施,那么这会使单元测试更困难。
- 由一个项目的服务引用生成的数据别想在其它任何项目中共享(即使是相同的服务)。对于编译器而言,不管名字如何,类型是不同的。
- WCF 使用接口的全部原因是抽象,但由于服务引用的工作机制,没有简单的方法可以抽象服务的实际用法。
Michaels 首先提供了 ClientBase
关于包装器,还有一点值得注意,那就是它实现了 IDisposable。 ClientBase
已经实现了这个接口,为什么我们还要实现它?因为基类型没有按照正确的方法实现这个接口。实现该接口的类应该提供一个可重写的方法供派生类实现,但该类没有这样做。为了修复清理代码,我们不得不重新实现该接口和具体的方法。不过,我们会正确地实现它,不管是在派生类型方面,还是在故障通道处理方面。
在其系列文章的第三部分,Michael 谈了如何减少使单个服务调用自成一行所需的样板文件。比如:
ServiceClientFactory.InvokeMethod<ServiceReference1.IEmployeeService>(c => c.Update(dlg.Employee));
这种模式确实也有其自身的局限性:
上述代码最大的问题可能是依赖于一个静态类。静态类在测试的时候不容易模拟,因此,在 WCF 服务可用之前,任何使用了静态类的代码都不容易进行单元测试。一个变通的做法是,创建一个虚方法,然后调用静态类。但这样做的结果是,仅仅为了达到单元测试的目的,就创建了大量的虚方法(可能每个服务方法都有一个)。
Michael 计划在以后的文章中纠正这些问题。
查看英文原文:**** Building a Better WCF Client
评论