[翻译]在WCF Services中共享DataContacts(正在进行中…)
Sharing DataContracts between WCF Services
在WCF Services中共享DataContacts
In a previous article, Multiple web service references sharing types,
I wrote about sharing types between referenced web services.
That article used the old ASP.NET Web Service technique,
but nowadays WCF is the technology to use. In this article
I will use much the same scenario as in the previous article,
Visual Studio 2010 and of course WCF (.NET 4).
在之前的文章《Multiple web service references sharing types》中,
我写到关于在引用web services时如何共享类型,
那篇文章使用了旧版的Asp.net web services技术,但是现在流行WCF,
在这篇文章中我会采用和之前文章类似的描述方式,
来讲述在WCF环境下如何来实现。
I will describe the scenario below, describe what may be an upcoming problem and what can be done to avoid it.
Please read the previous article as well, it is much the same solution that is to be presented here.
接下来我会描述一下实现方案,说明一下可能出现的问题以及如何来避免它。请大家也阅读一下前一篇文章,
解决方案大致比较相似,这里也将会提到。
If you are not familiar with WCF and DataContracts you can read David Chappell’s article here:
Introducing Windows Communication Foundation in .NET Framework 4.
Aaron Skonnard has written a great article about WCF 4,
read it here: A Developer’s Introduction to Windows Communication Foundation 4.
如果你不熟悉WCF和DataContracts,
你可以阅读一下David Chappell写的《Introducing Windows Communication Foundation in .NET Framework 4》,
Aaron Skonnard曾经写过一篇更好的介绍WCF4的文章,在这里可以看到:
《A Developer’s Introduction to Windows Communication Foundation 4》.
The scenario
具体方案:
Let’s consider the following scenario:
下面我们就来说明这种方案:
One WCF service exposes a number of methods, in this example there will be only two:
一个WCF服务会暴露一些方法给用户使用,在这个例子中会有两个公开方法:
· GetCustomer: takes as parameter an integer, an ID of a customer, and returns the customer
with that ID from the repository. GetCustomer: int型参数,customer的ID,根据ID从库中返回对应的customer对象. · GetCustomerStatus: takes as parameter a customer and returns a string based on the age of the customer
that tells the client whether the customer is allowed to place orders or not.
GetCustomerStatus:以customer作为参数,返回一个基于customer(客户)年龄的字符串,通知客户端这个customer(客户)是否允许下订单.
The interface exposed by the service is shown below. Please note the attribute [ServiceContract] on the interface and [OperationContract] on the methods.
服务通过接口暴露给下方,在接口声明上需要加上[ServiceConcact]标记,在方法声明上加上[OperationContact]标记.
服务定义
These two methods uses the same DataContract. A DataContract in the WCF world is a class defining a type exposed by the service.
This class may contain a lot of stuff, methods and functions.
The properties to be exposed to the consumers are marked with an attribute, [DataMember].
These properties may include logic in their get and set methods. The Customer DataContract is shown here, see the comments below the code:
这两个方法采用了相同的数据契约(DataContact),数据契约在WCF中是服务暴露的一种自定义类型,这个类可以包含很多属性,
方法和函数.属性通过标记为[DataMember]来暴露给用户.
这些属性可以在它们的get和set方法中包含一些逻辑处理.下面展示了Customer DataContact如何定义的,
方法作用请查看代码中注释部分。
数据契约定义
Some notes on the code above:
关于以上代码的部分注解:
· I have decided to place the Customer class in a separate class library, the namespace is therefor ServicesDataContracts.
我决定将Customer类放置到一个单独的类库中,命名空间为ServicesDataContracts。
· On line 5: the System.Runtime.Serialization namespace contains the DataContract attribute.
Please note that it is also required to add a reference to System.Runtime.Serialization to use the DataContract attribute.
在第5行,System.Runtime.Serialization 命名空间包含了DataContract特性的定义。
需要注意的是,使用它仍需要添加System.Runtime.Serialization的引用。
· The DataMember attribute is added to the properties to be exposed by the service DataContract.
In this example these properties are ID, Name and BirthDate.
属性上添加DataMember特性标记表示它被暴露给服务。(备注:可以被客户端访问)
在这个例子中包含ID,Name和BirthDate属性被标记。
· The property IsAllowedToShop is not exposed.
IsAllowedToShop属性没有被暴露。(不可被客户端访问)
· The function GetStatusMessage is not exposed.
GetStatusMessage 函数(方法)没有被暴露。
· The properties exposed by the DataContract may contain logic in the get and set methods,
but this is not transferred to the client when adding a service reference to client project.
通过DataContract标记暴露的属性可以在get和set方法中包含逻辑处理,
但是在添加服务引导到客户端时候并不会把这些传递过去。(我理解为在客户只能看到属性而看不到逻辑处理)
A consumer of the service and the DataContract above may look like this:
一个客户端引用服务像以下使用:
演示代码
This consumer holds a reference to the service through the CustomerService namespace.
客户端通过CustomerService的命名空间保持一个服务的引用,
The namespace is defined when adding the service reference.
这个命名空间在添加服务引用的时候被定义。
So far the service interface is small and tiny, but it will for sure grow over time.
到目前为止这个服务接口是比较小的,而且功能比较薄弱的,但是它无疑会随着时间的推移慢慢壮大的。
It comes to a point when it is hard to maintain the service and therefor it may be necessary to split it into a number of smaller services.
当服务汇集到一个点时它就很难维护,这个时候就有必要将它分拆成多个小型的服务。(这句话我自己理解就是当服务比较庞大时,就有必要根据它实现的功能分拆成一个个的小服务,实现职责分离。)
Take a look at the picture below, it may make things clearer what I want to do with the WCF Service.
看下面这幅图片,它可以把我想让WCF Service做的事情展示的更容易让人明白。
The service consumer shall be able to pass objects from one service to another, without any casting.
The split and types sharing
The scenario stated above is trivial and the service interface is small, but still let’s have a look at how to split it.
The same procedure when splitting is probably applicable on larger services with a lot of DataContracts. The following will be done:
· Create a service exposing the GetCustomer method. This service holds the customer repository, in this case it will be a fake repository.
· Create a service exposing the GetCustomerStatus method.
· The two services will use the same DataContract, the Customer class shown above.
· Create a DataContract to be distributed to the client(s) (I will describe why below).
· Create a client application, referencing the two services.
So why should the DataContract be distributed separately to the service consumers? When adding service references to the two services created,
the Customer class will be of different types depending on what service the client will use.
This makes it harder to get a customer from the service exposing the GetCustomer method and pass this customer on to the service exposing the GetCustomerStatus.
The customer returned by the first service must be casted to a customer object type handled by the second service. In this example the customer class is small,
but it would still be nice to just be able to pass the object on to the next service.
The first two things in the list above is easy, add two WCF Service Applications to the Visual Studio solution.
Add the methods from the interface above to the new services,
the GetCustomer method goes into the first interface and the GetCustomerStatus method goes into the second one.
Both services need to reference the DataContract class library, the same library used above. After that, compile the solution.
The trick to make the services share the same types on the client side is to have the DataContract in a separate class library.
This makes it possible to use the svcutil tool that comes with Visual Studio. With that tool you can extract the DataContracts defined in a class library,
so let’s do it.
DataContract extraction
Open a Visual Studio command prompt and execute the following command, the output is shown below:
svcutil /dconly <path to the output DataContract class library dll>
This command will extract the DataContracts from the .dll file and create a number of .xsd files.
The .xsd files can be distributed to the service consumer teams around, that will consume and use theses DataContracts.
In this example I will use the .xsd file with the same name as the DataContract class library .dll file and generate a class file to be included in the client project.
So let’s do this. Note that the .xsd file in my case is called ServicesDataContracts.xsd since the class library holding the contract is called ServicesDataContracts.
The out parameter lets us decide the name of the class file to be generated. The output is shown below.
svcutil /dconly /language:CS ServicesDataContracts.xsd /out:CustomerDataContracts.cs
The class file generated by the above command line is added to a client side class library project.
In my case I decided to call this project ConsumerDataContracts.
Adding service references
Now it’s time to add service references to the two services created above.
But before that, let the client application reference the class library where the ConsumerDataContracts class was added.
The solution will look like this:
There you can see the DataContracts class library and the reference to that from the ServiceConsumer project.
In my case I used a Windows Forms application to consume the services.
Now it’s time to add the service references. Right-click and select “Add Service Reference…”.
Enter the address to the first service, give it a namespace of your choice (I entered “CreatorService”),
click the Advanced button and make sure to select “Reuse types in specified referenced assemblies”
and after that select the ConsumerDataContracts reference. This looks like this:
Click OK. The service reference will now be added to the project. Repeat these things adding a reference to the second WCF service.
I called the second service reference ControllerService. The solution will now look like this:
Since we added the services with the reuse types settings like this,
the two service references will use the Customer DataContract in the ConsumerDataContracts.
Now we’re done, it is now possible for the consumer to make calls to the services like this:
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);
Some notes on the code above:
· On line 1 and 2: The CreatorService client is created
· On line 3: A customer object is returned from the CreatorService
· On line 5 and 6: The ControllerService client is created
· On line 7: The customer object is passed to the ControllerService
The service method invocation on line 7 wouldn’t be possible, it would generate a compilation error,
if we hadn’t reused the types when we added the service references earlier.
What just happened?
I will try to summarize what we have done:
· We wanted to split an existing WCF service into smaller parts.
· We wanted the smaller services to share types on the client side
· We created two smaller services using the same datacontract class library as the larger one
· We extracted the datacontracts and added them to a client class library project
· We added service references to the client project and let them reuse types from the client class library project where we added the extracted datacontracts
· We tried the services out by getting a customer object from one service and pass this on as a parameter to another service
No casting is needed and the client code will be very clean and nice. The code on the service side is exactly the same as before the split,
the only difference is that half the code goes into one service and the other half goes into the other service.
As soon as I have solved the problems to attach files to a post, I will publish the solution I used to try this out.
This solution contains both the service before and after the split. The service consumer consists of two forms,
one that uses the “large” service and one that uses the two smaller services.
原文地址:
http://www.freddes.se/2010/05/02/sharing-datacontracts-between-wcf-services/
说明:
这是自己遇到的一个问题,在网上看到外国友人分享的心得,本人英文水平有限,
只能慢慢翻译,也请各位英文达人能给纠正其中翻译不准备的地方,
我也当是学习英文,文章当前没有翻译完,我会慢慢翻译,
争取用比较容易理解的方式翻译出来,
如果各位有兴趣也可以跟帖列出文章中尚未翻译的部分,
标出中文和对应英文,收录以后我会在翻译后备注各位的网名。
本人英文有限,各位拍砖轻些。
(红字部分为译文,其他为原文)