A site devoted to discussing techniques that promote quality and ethical practices in software development.

Sunday, December 5, 2010

Using WCF to produce Web Service Contract documents that must use a supplied schema

This post is to describe a very common scenario in SOA/Web Service world. To avoid chaos in SOA world, practitioners are encouraged to use "Standardization of Service Data Representation" so that services and clients are communicating using standardized or common vocabulary. They may be standards data representations specified within an enterprise or by trade groups like MIMOSA and are not necessary standard endorsed by W3C.

The problem at hand is to produce service contract documents that a system needs to interact with abstractly and the data interchanges must use a standardized or common representation. For ease of discussion, let's assumed the standardized data is from ACME Enterprise and supplied in schema file called ACMEEnterprise.xsd.

One way to do this is to use a process called Contract-First or Schema-First. This gives the designer the maximum control in what to put into the Service Contract. Service Contract, include WSDL and XSD, is not just for machine to execute but also containing information that are useful to service producers and consumers. However, it is not for the faint-hearted; it is only for the most determined soul.

Instead of using Contract-First, in this post, I am describing the necessary steps and settings of using WCF to generate Service Contract documents that use the prescribed data representation. In the frequently described scenarios, the developer is responsible for specifying the data and service contract. But in the problem at hand, much of the data representation or data contract, are predetermined.

Designing a Service Contract using WCF conforming to supplied data representation

As stated previously, the data representation is supplied in ACMEEnterprise.xsd and the targetNamespace is "http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd". All data or message exchanges must use types specified in this schema file. The Service Contract many specify other data contracts it sees fit but if it is to describe data for ACME Enterprise, it must use the types specified in ACMEEnterprise.xsd.

Step 1 - Produce the .Net classes

The first step is to convert the types specified in the ACMEEnterprise.xsd into .Net classes that we can use in WCF constructs. For illustration purpose, I use C# but you can use any other .Net languages.

This can be achieved by using XSD.exe or SvcUtil.exe. Most WCF materials will recommend one to use SvcUtil.exe but if the schema is specified using the full set of W3C XSD Schema syntax, the chance that SvcUtil can process your schema file is slim. The reason is that SvcUtil is designed to work with DataContractSerializer which maps the CLR data types to XSD's or vice versa and that the Data Contract model only supports a limited subset of the W3C Schema specifications.

You have a better chance of successful conversion by using XSD.exe provided that you follow the caveat. What CLR namespace you choose to use is immaterial and is not something that will affect the wire format. They are local artifacts affecting only your .Net solutions.

Step 2 - Incorporate the generated file into your project

The next step is to incorporate the generated file into your project and begin using the types in that files to design your Service Contract as if you are dealing with normal WCF DataContract types.

The main thing to note is that each class generated is adored with the XmlTypeAttribute declaring the Namespace corresponding to the targetNamespace in ACME, like this:
    public partial class Employee : Person {
        // . . . 

It is vitally important that this namespace is maintained when we generate the Service Contract documents for this type.

Step 3 - Mark the Service Contract with XmlSerializerFormatAttribute

This is a very important point. You may apply this attribute to only those Contract Operations that require to use XmlSerializer. In my case since every operation is using this serializer, I apply this attribute to the entire service contract. If you do not apply this attribute, types that came from ACMEEnterprise.xsd will be placed under the targetNamespace of "http://schemas.datacontract.org/2004/07/ACME.Enterprise" like this:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://schemas.datacontract.org/2004/07/ACME.Enterprise" 
  <xs:complexType name="Person">
      <xs:element name="ageField" type="xs:int" />
      <xs:element name="firstNameField" nillable="true" type="xs:string" />
      <xs:element name="genderField" type="tns:Gender" />
      <xs:element name="hobbyField" nillable="true" type="xs:string" />
      <xs:element name="lastNameField" nillable="true" type="xs:string" />
      <xs:element name="secretNumberField" nillable="true" type="xs:long" />
This effective produces a different type on the wire. The use of XmlSerializerFormatAttribute retains the original targetNamespace.

Step 4 - Build the WCF Service Library and Generate the Contract documents

After you have finished building the WCF Service Library you can use SvcUtil.exe to produce the Service Contract documents. The process will generate the WSDL as well as the companion XSD files. While this process with the aid of XmlSerializerFormatAttribute preserves the targetNamespace, as shown below, for the types that are used in this service library and placing them in a XSD file resembling the original XSD file, the process is at best of low fidelity. That is the process loses information that are in the original documents deem not needed in WCF. Information such as <annotation>, <documentation>, and <restriction> elements are lost. The schema file only contains a subset of the types described in the original schema; it includes only types used in the service.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd" 
  <xs:complexType name="Person">
      <xs:element minOccurs="0" maxOccurs="1" name="FirstName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="LastName" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Age" type="xs:int" />
      <xs:element minOccurs="1" maxOccurs="1" name="Gender" type="tns:Gender" />
      <xs:element minOccurs="1" maxOccurs="1" name="SecretNumber" nillable="true" type="xs:long" />
      <xs:element minOccurs="1" maxOccurs="1" name="Hobby" nillable="true" type="xs:string" />

You may replace the regenerated schema file for types for ACME Enterprise with the original one without problem so that it contains all the valuable information.

The process described here is a low fidelity process. It does not allow designer to include annotation in the WSDL file. The only way to create a full fidelity Service Contract is to use Contract-First process by authoring the messages and then the WSDL. This will be reported in full post.

"SOA Principle of Service Design" Section 6.3 "Types of Service Contract Standardization"

No comments:

Blog Archive