动态生成 WCF 代理






4.04/5 (13投票s)
使用动态生成的 WCF 代理节省时间。
目录
引言
大多数程序员在开始使用 Windows Communication Foundation (WCF) 时,都会使用 Microsoft 提供的示例和教程。所有这些示例都基于静态生成的服务引用或代理。当您想从客户端连接到服务器时,您可以使用 Visual Studio 的添加服务引用功能,从客户端创建到服务器项目的引用。然后 Visual Studio 会连接到服务器,提取服务器的元数据,并生成一个代理。客户端使用此代理来实际连接并调用服务器上的方法。
大多数程序员不知道 WCF 还有另一种生成此代理的方式,那就是动态生成。这种动态生成代理的方式有很多优点。
背景
众所周知,有时在开发过程中,服务的接口会发生变化。服务的配置也可能发生变化;例如,服务监听的端口号会发生变化。现在,假设一个开发人员更改了服务接口或配置,但不知何故忘记更改客户端(这种情况发生在我身上不止一次)。使用静态生成的代理,您会在开发周期的后期发现错误,很可能是在第一次测试时。
这种情况发生在我身上很多次,促使我思考是否还有其他方法。如果在本地工作站编译时,或者在构建服务器上进行构建时,就能解决这个问题,那岂不是更好?使用动态生成的代理,这是可能的。
构建程序集结构
为了能够使用动态代理及其优势,您需要以稍有不同的方式构建您的程序集或类。客户端和服务器都引用并使用一个独立的程序集,该程序集包含 WCF 服务的接口和配置。下图展示了这个想法。
名为“接口 + 配置”的程序集包含了服务器的 WCF 配置,包括接口本身。通过这种方式构建程序集,当接口发生变化时,就不可能忘记更改客户端。它根本无法编译!
Using the Code
源代码包含三个程序集:服务器、客户端以及接口/配置程序集。它们包含在一个 Visual Studio 2008 解决方案中。当您运行应用程序时,服务器和客户端都会启动。客户端向服务器发送一些消息。
使用 WCF 生成动态代理并不困难,WCF 中的 ChannelFactory
使得这成为可能。生成动态代理的源代码如下所示。
客户端
EndpointAddress endpointAddress =
new EndpointAddress(Configuration.CreateServerUriString());
ChannelFactory<imessagesender> channelFactory =
new ChannelFactory<imessagesender>(Configuration.CreateBinding(), endpointAddress);
IMessageSender messageSender = channelFactory.CreateChannel(endpointAddress);
messageSender.ShowMessage("Test message");
((IChannel)messageSender).Close();
ChannelFactory
使用 IMessageSender
接口通过 CreateChannel
方法创建通道。IMessageSender
和 Configuration
接口都来自共享程序集。
服务器端也使用来自 Configuration
类的这些信息。它创建一个新的 ServiceHost
,并在 Configuration
中的 Uri
上进行监听。
服务器
Server singleServerInstance = new Server();
Uri baseUri = new Uri(Configuration.CreateServerUriString());
ServiceHost serviceHost = new ServiceHost(singleServerInstance, baseUri);
serviceHost.AddServiceEndpoint(typeof(IMessageSender),
Configuration.CreateBinding(), baseUri);
serviceHost.Open();
Console.ReadLine();
serviceHost.Close();
Configuration.CreateBinding()
方法创建绑定。此方法返回一个在 Configuration
类中配置的特定绑定实例。如果您更改服务器的绑定,例如从 HTTP 更改为 TCP,客户端和服务器都会自动使用它。
关注点
这个解决方案并非是高深的学问,但它在过去为我和我的公司节省了时间。这也是我希望与您分享这些信息的原因。当有人在接口更改后忘记更改客户端时,解决方案将无法编译。这样,它会快速失败,这是一件好事。另一个优点是,您可以停止发布服务的元数据,这将提高您服务的安全性。
集中式绑定配置
与配置一起,绑定也集中在配置程序集中进行配置。这使得您可以为客户端和服务器配置绑定。例如,您可以设置 MaxReceivedMessageSize
和 ReceiveTimeout
,这些应该同时为客户端和服务器设置。通过集中配置,现在可以在一个地方而不是两个或多个配置文件中完成此操作。下面的代码展示了如何执行此操作的示例。
public static class Configuration
{
public static Binding CreateBinding()
{
NetTcpBinding binding = new NetTcpBinding();
binding.MaxReceivedMessageSize = 25 * 1024 * 1024;
binding.ReceiveTimeout = new TimeSpan(0, 2, 0);
return new NetTcpBinding();
}
}
配置的进一步扩展
通过标准化您的服务配置,可以进一步扩展它。例如,您可以为所有配置创建一个标准的基类或接口。这种标准化使得从您的配置中提取信息成为可能,例如文档。这将能够从您的配置类自动创建文档。结果是所有服务的完整且最新的文档。一个好的开始方法是定义一个接口,其中包含所有要记录的属性的方法。请参阅下面的代码示例。
public interface IWcfConfiguration
{
string RetrieveUriString();
int RetrievePortNumber();
TimeSpan RetrieveTimeOut();
}
使用回调
使用此解决方案可以实现回调。在使用回调时,客户端中使用的 ChannelFactory
应更改为 DuplexChannelFactory
。DuplexChannelFactory
的构造函数需要一个实例,服务器可以在其中调用回调方法。客户端还应调用订阅和取消订阅方法来添加回调方法。请参阅下面的代码示例。下载中提供的源代码包含一个使用回调的示例。
DuplexChannelFactory duplexChannelFactory =
new DuplexChannelFactory(this, binding, endpointAddress);
IMessageSenderWithCallBack duplexMessageSender =
duplexChannelFactory.CreateChannel(endpointAddress);
duplexMessageSender.Subscribe();
duplexMessageSender.ShowMessage("message");
duplexMessageSender.Unsubscribe();
缺点
此解决方案也有其缺点。它仅在您控制通信双方时才有效。如果您发布一个供公司外部使用的服务,情况就不同了。您可以发布接口和配置程序集,但我认为在这种情况下,启用您服务上的元数据会更容易。
修订历史
- 22/05/2010
- 第一版
- 06/01/2011
- 在文章和提供的源代码中添加了回调的说明。感谢 Farhad Bheekoo 提出问题并提供示例源代码。