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

动态生成 WCF 代理

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.04/5 (13投票s)

2010年5月22日

CPOL

5分钟阅读

viewsIcon

133071

downloadIcon

2558

使用动态生成的 WCF 代理节省时间。

目录

引言

大多数程序员在开始使用 Windows Communication Foundation (WCF) 时,都会使用 Microsoft 提供的示例和教程。所有这些示例都基于静态生成的服务引用或代理。当您想从客户端连接到服务器时,您可以使用 Visual Studio 的添加服务引用功能,从客户端创建到服务器项目的引用。然后 Visual Studio 会连接到服务器,提取服务器的元数据,并生成一个代理。客户端使用此代理来实际连接并调用服务器上的方法。

大多数程序员不知道 WCF 还有另一种生成此代理的方式,那就是动态生成。这种动态生成代理的方式有很多优点。

背景

众所周知,有时在开发过程中,服务的接口会发生变化。服务的配置也可能发生变化;例如,服务监听的端口号会发生变化。现在,假设一个开发人员更改了服务接口或配置,但不知何故忘记更改客户端(这种情况发生在我身上不止一次)。使用静态生成的代理,您会在开发周期的后期发现错误,很可能是在第一次测试时。

这种情况发生在我身上很多次,促使我思考是否还有其他方法。如果在本地工作站编译时,或者在构建服务器上进行构建时,就能解决这个问题,那岂不是更好?使用动态生成的代理,这是可能的。

构建程序集结构

为了能够使用动态代理及其优势,您需要以稍有不同的方式构建您的程序集或类。客户端和服务器都引用并使用一个独立的程序集,该程序集包含 WCF 服务的接口和配置。下图展示了这个想法。

How to divide assemblies to use a dynamic WCF proxy

名为“接口 + 配置”的程序集包含了服务器的 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 方法创建通道。IMessageSenderConfiguration 接口都来自共享程序集。

服务器端也使用来自 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,客户端和服务器都会自动使用它。

关注点

这个解决方案并非是高深的学问,但它在过去为我和我的公司节省了时间。这也是我希望与您分享这些信息的原因。当有人在接口更改后忘记更改客户端时,解决方案将无法编译。这样,它会快速失败,这是一件好事。另一个优点是,您可以停止发布服务的元数据,这将提高您服务的安全性。

集中式绑定配置

与配置一起,绑定也集中在配置程序集中进行配置。这使得您可以为客户端和服务器配置绑定。例如,您可以设置 MaxReceivedMessageSizeReceiveTimeout,这些应该同时为客户端和服务器设置。通过集中配置,现在可以在一个地方而不是两个或多个配置文件中完成此操作。下面的代码展示了如何执行此操作的示例。

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 应更改为 DuplexChannelFactoryDuplexChannelFactory 的构造函数需要一个实例,服务器可以在其中调用回调方法。客户端还应调用订阅和取消订阅方法来添加回调方法。请参阅下面的代码示例。下载中提供的源代码包含一个使用回调的示例。

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 提出问题并提供示例源代码。
© . All rights reserved.