[翻译]在WCF 服务中共享DataContacts

Sharing DataContracts between WCF Services

在WCF Services中共享DataContacts

在之前的文章《Multiple web service references sharing types》中,

我写到关于在引用web services时如何共享类型,

那篇文章使用了旧版的Asp.net web services技术,但是现在流行WCF,

在这篇文章中我会采用和之前文章类似的描述方式,

来讲述在WCF环境下如何来实现。

接下来我会描述一下实现方案,说明一下可能出现的问题以及如何来避免它。请大家也阅读一下前一篇文章,

解决方案大致比较相似,这里也将会提到。

如果你不熟悉WCF和DataContracts,

你可以阅读一下David Chappell写的《Introducing Windows Communication Foundation in .NET Framework 4》,

Aaron Skonnard曾经写过一篇更好的介绍WCF4的文章,在这里可以看到:

A Developer’s Introduction to Windows Communication Foundation 4》.

具体方案:

下面我们就来说明这种方案:

一个WCF服务会暴露一些方法给用户使用,在这个例子中会有两个公开方法:
GetCustomer: int型参数,customer的ID,根据ID从库中返回对应的customer对象.
GetCustomerStatus:以customer作为参数,返回一个基于customer(客户)年龄的字符串,通知客户端这个customer(客户)是否允许下订单.

 

服务通过接口暴露给下方,在接口声明上需要加上[ServiceConcact]标记,在方法声明上加上[OperationContact]标记.

服务定义

这两个方法采用了相同的数据契约(DataContact),数据契约在WCF中是服务暴露的一种自定义类型,这个类可以包含很多属性,

方法和函数.属性通过标记为[DataMember]来暴露给用户.

这些属性可以在它们的get和set方法中包含一些逻辑处理.下面展示了Customer DataContact如何定义的,

方法作用请查看代码中注释部分。

数据契约定义

关于以上代码的部分注解:

我决定将Customer类放置到一个单独的类库中,命名空间为ServicesDataContracts。

在第5行,System.Runtime.Serialization 命名空间包含了DataContract特性的定义。

需要注意的是,使用它仍需要添加System.Runtime.Serialization的引用。

属性上添加DataMember特性标记表示它被暴露给服务。(备注:可以被客户端访问)

在这个例子中包含ID,Name和BirthDate属性被标记。

  • IsAllowedToShop属性没有被暴露。(不可被客户端访问)
  • GetStatusMessage 函数(方法)没有被暴露。

通过DataContract标记暴露的属性可以在get和set方法中包含逻辑处理,

但是在添加服务引导到客户端时候并不会把这些传递过去。(我理解为在客户只能看到属性而看不到逻辑处理)

一个客户端引用服务像以下使用:

演示代码

客户端通过CustomerService的命名空间保持一个服务的引用,

这个命名空间在添加服务引用的时候被定义。

到目前为止这个服务接口是比较小的,而且功能比较薄弱的,但是它无疑会随着时间的推移慢慢壮大的。

当服务汇集到一个点时它就很难维护,这个时候就有必要将它分拆成多个小型的服务

(这句话我自己理解就是当服务比较庞大时,就有必要根据它实现的功能分拆成一个个的小服务,实现职责分离。)

看下面这幅图片,它可以把我想让WCF Service做的事情展示的更容易让人明白。

wps_clip_image-24020

此服务客户端可以将对象通过一个服务传到其他的服务,而无需转换。

分离以及类型共享

由于这个服务接口是小规模的服务,以上所述的方案也显得比较零碎,但是,我们仍然坚持来看一下如何来分离它。

The same procedure when splitting is probably applicable on larger services with a lot of DataContracts.

(这句不知道怎么翻译好,总觉得读着挺难过的)当为一个相同的程序拆分一个包含多个DataContract的大型服务的时候,

The following will be done:

将完成以下操作:

  • 创建一个暴露GetCustomer方法的服务,这个服务持有Customer库,在这种情况下它是个虚拟的库。
  • 创建一个暴露GetCustomerStatus方法的服务。

这两个服务会共用一个DataContract,就是上面提到的Customer类。

为客户端建立一个分布式的DataContract(下面我会讲为什么这么做)

建立一个客户端应用程序,引用这两个服务。

所以为什么这个DataContract被服务用户使用的时候要被声明成分布式的呢?

当添加这两个已经被创建的服务引用时,

这个Customer类会根据客户端使用不同的服务而被声明为两个不同的类型,(2个服务引用产生不同的命名空间,从而Customer类就变成了“两个”不同的类了)

这将导致很难从提供了GetCustomer方法的服务以及提供了GetCusomerStatus方法的服务中获取到Customer对象。(因为这个服务被解析到了两个不同的命名空间中)

第一个服务返回的Customer对象必定会忽略第二个服务返回的Customer类型对象。在这个例子中,Customer类是个比较小的类,

但是它依然可以比较好的通过对象传递给下一个服务。

在Visual Studio解决方案中,通过添加两个WCF服务应用,就可以很容易的解决上述列出的两件事情。

在这个新的服务接口上添加方法,GetCustomer方法继承自第一个接口,GetCustomerStatus方法继承自第二个接口。

这两个服务需要引用一个相同的数据契约类库,做完这些,编译整个解决方案。

这些服务在客户端共享这个相同的数据契约类型的诀窍就是这个类库必须要在一个单独的类库中。

这样就使它可以使用Visual Studio的svcutil工具,用那个工具你可以在一个类库中提取数据契约的定义。

所以,让我们来这么做。

DataContract extraction

数据契约释放(这个不知道如何翻译好)

打开一个VisualStudio命令行工具,执行以下的命令,输出如下:

svcutil /dconly <path to the output DataContract class library dll>

wps_clip_image-23209

这个命令会从.dll文件中提取数据契约,然后创建一些.xsd文件。

这些xsd文件可以被分发到各个服务的消费者队伍,这将耗尽这些数据契约。(怎么都感觉不太通顺)

在这个例子中,我会使用相同名称的数据契约dll类库的xsd文件,并在客户端生成一个类文件。

现在让我们这样做。注意,在我的实例中支持这个契约的被称作ServiceDataContracts.xsd的.xsd文件被称为 ServicesDataContracts 的类库。(也不是很通顺)

(以下命令)输入的参数让我们决定了产生的类文件名称,输入如下显示:

svcutil /dconly /language:CS ServicesDataContracts.xsd /out:CustomerDataContracts.cs

wps_clip_image-32363

通过上面的命令行产生的类文件将被添加到客户端的类库项目中。

在我的这个实例中我决定调用这个ConsumerDataContracts项目。

添加服务引用

现在是时候添加我们以上创建的两个服务引用了。

但是在这之前,在ConsumerDataContracts类被添加之前,让客户端引用这个类库。

解决方案将会像这样:

wps_clip_image-16207

这里你就可以看到这个数据契约类库和从ServiceConsumer项目添加的引用。

在我的这个实例中,我会使用一个一个windowsForms应用来使用这些服务。

现在是时候添加这些服务引用了。右键选择“添加服务引用”。

输入第一个服务的地址,给你选择的指定一个命名空间(我输入的是“CreatorService”)

单击高级按钮确定选择“重用指定类型”

在这之后选择ConsumerDataContracts引用。像这样:

wps_clip_image-18261

单击确定。这个服务引用就会被添加到项目中。重复这个操作添加第二个Wcf服务引用。

我给第二个引用的服务命名为ControllerService。这个解决方案会像这样:

wps_clip_image-8605

自此我们便添加了像我们设置的重复类型的服务。

这两个服务引用会使用ConsuumerDataContracts中的Customer数据契约。

到现在为止,我们就算完成了,现在就可以这样调用这些服务:

74 CreatorService.CreatorClient creatorServiceClient =

75 new CreatorService.CreatorClient();

76 Customer customer1 = creatorServiceClient.GetCustomer(1);

77

78 ControllerService.ControllerClient controllerServiceClient =

79 new ControllerService.ControllerClient();

80 string statusText = controllerServiceClient.GetCustomerStatus(customer1);

关于以上代码的注解:

  • 在第一行和第二行:客户端CreatorService被创建
  • 在第三行:一个customer对象被CreatorSercie返回
  • 在第五行和第六行:客户端ControllerService被创建
  • 在第七行:这个customer对象被传递给ControllerService

第七行的服务调用方法是不可能的,它会产生一个编译错误。

如果我们不能更早的添加服务引用又应该如何重用这些类型呢,

这中间发生了什么?

我尝试总结一下我们刚刚所做的:

  • 我们想将一个已经存在的WCF服务分拆成一些小的部分。
  • 我们想在让这些服务在客户端共享类型。
  • 我们创建了两个使用了相同数据契约类库的小型服务来代替那个大的服务。
  • 我们提取这些数据契约添加他们到客户端类库项目中。
  • 我们往客户端添加服务引用让他们重用客户端类库中我们添加的数据契约类型。
  • 我们尝试让我们的服务从一个服务获取的customer对象作为一个参数传递到另外一个服务。

没有强制类型转换会使客户端代码非常干净整洁,服务端代码完全与拆分前相同。

唯一的区别是一半的代码添加到一个服务,另一半在其他服务中。

我已经在一篇文章中解决了这个问题,我将发表我用来尝试执行这个问题的解决方案。

这个解决方案包含这两个服务拆分之前和拆分后两种形式。这个服务被应用在两个form中,

一个使用了“大”的服务,一个使用两个较小的服务。

原文地址:

http://www.freddes.se/2010/05/02/sharing-datacontracts-between-wcf-services/

 

后记:

断断续续终于把这篇文章翻译完了,因为本人英文水平有限,翻译难免有不妥的地方,欢迎大家批评指正,如果有觉得不明白的地方,可以参考一下原文,

我也会根据大家的指正修正其中错误。其中蓝色部分不是翻译后的文字,只是加入了我个人的理解。

谢谢。

原文地址:https://www.cnblogs.com/boyliupan/p/2031403.html