Share via



October 2009

Volume 24 Number 10

Contract-First Web Services - Schema-based Development with Windows Communication Foundation

By Christian Weyer | October 2009

"If I had eight hours to chop down a tree, I'd spend six sharpening the axe" said Abraham Lincoln. This is true for different phases in software engineering as well. Having a well-thought-through design is the secret behind most software success stories. This is especially true when it comes to designing communication systems such as Web Services (WS). Careful attention should be paid toward the formal interface used for communication. This interface dictates the usability and interoperability of your system. Consequently, designing this interface -- which is also known as the contract -- during early phases of the lifecycle is significant. In this article, we will show you how to design and develop contracts first for your Windows Communication Foundation (WCF)-based services, focusing on the area of Web services. With WS contracts, two major methodologies exist -- Code-first contract-first development and Schema-first contract-first development. Our focus will mainly be on the latter.

What is Contract-First Development?

Contract-first design and development is not something new. It was formally introduced by Bertrand Meyer as a part of his Eiffel Programming Language design and has appeared in various technical publications since 19861,2. Therefore, understanding contracts from an old-school tool/technology perspective might be helpful to grasp why it it's useful.

Although computers today can do a lot of things from simple arithmetic to controlling the satellites orbiting our earth, the fundamental idea of an input-output machine hasn't changed since adding machines invented in 19th century. Consequently, software engineers still write functions that take some input and perform some work and output something. These functions are then used from other places. A contract of a function defines the expectations and commitments of that function. In other words, input parameters to a function can be thought as its expectations and the return values can be considered as its commitment. Users of the function only need to know about the contract to use it. A classic example for this is a C++ header file. When you want to invoke a function in another C++ library, you are not interested in looking at the implementation -- in fact, most of the time, you would not have access to the implementation. The plain text header file tells you and your compiler enough about what you need to invoke that function and what you will get at the completion of it.

Type library files in COM and ActiveX, and interfaces in C#, are commonly seen as other forms of contracts. Because a contract is a way of defining formal, accurate, verifiable interface specifications based on abstract data types for software components, there's a natural tendency to create the contracts first. That's why almost every C/C++ programmer starts a program by first writing the header file.

Contracts in WS are no exception. As a WS developer, your intention is to share your input/output system as a Web service -- potentially across platform boundaries. We are sure you've heard many times before that the users of your Web service should not be worrying about your implementation details. Having access to your contract should be sufficient for someone else to consume it. Because Web services are platform-independent, we use interoperable, standards-based constructs like Web Service Definition Language (WSDL) and XML Schema (XSD) to define contracts. Modeling a contract involves modeling data, messages and the interface of your Web service.

First, you model your data contracts (that is, data structures) that are used to pass data between the clients and the service. Data contracts could either be simple or primitive types such as string, integer, double or complex, or domain-specific types such as Product, Employee or Itinerary.

RPC/Encoded style uses types defined in schema to define the parts of WSDL messages. This makes it difficult to validate the entire SOAP body against the schema, as only a part of it represents what’s defined in schema. The rest comes from the WSDL. It also encodes the SOAP body content as per section 5 encoding rules defined by the SOAP 1.1 specification. Although this style is legal, this is not WS-I-compliant.

RPC/Literal style is quite similar to RPC/Encoded style. However, unlike RPC/Encoded style, it does not encode the SOAP body. Although this style is WS-I-compliant, it is still difficult to validate.

Document/Literal uses elements to define parts of WSDL messages and does not encode the body content. This eliminates both of the main issues in previously discussed styles. Thus this has become the widely accepted WS-I-compliant style.

Document/Literal/Wrapped style was introduced by Microsoft. It’s the same as Document/Literal style, but it also wraps the content of the element inside an element created by the name of the operation. Having the operation name in the body makes it easier to dispatch messages from a non-HTTP endpoint. But more importantly, it helps to be compliant with the WS-I standard that demands only one child element inside element.

Typically, Web services interact with their clients by exchanging SOAP messages3. Modeling these message contracts is the second step of contract-first development. How you do that depends on which SOAP messaging format you prefer to use:

  • RPC/Encoded
  • RPC/Literal
  • Document/Literal
  • Document/Literal/Wrapped

There's also another format called Document/Encoded, but it's extremely hard to find implementations using it. For this article, we are going to focus only on Document/Literal/Wrapped because it's the most commonly used and it's also Web Services Interoperability Organization (WS-I)-compliant.

Defining a message contract has two aspects. First, it should define the structure of the SOAP body. You can use XSD to do this and you can also use data contracts you defined in the previous step. The other aspect of the message contract is defining the structure of soap headers. Headers for your messages are defined in WSDL. It's a common practice to use data contracts, modeled in step one, to define what goes in those headers.

Once you have your data and message contracts, you can model your interface by defining one or more operations available in the service. An operation contract may use message contracts modeled in the second step to define what messages are exchanged during that operation.

In addition to three primary contract types -- data, message and interface contracts -- a Web service contract also has a policy, bindings and endpoints. Figure 1 summarizes which WSDL/schema constructs are used to represent different artifacts in Web service contracts.


Figure 1 WSDL and XSD Constructs Usage

Code-First vs. Schema-First

As mentioned in the introduction, there are two ways to model your contracts - code-first and schema-first. It's important to understand both in order to pick the one that suits your needs.

In the code-first approach, you have the ability to use powerful declarative contract programming constructs provided by different Web service stacks (WCF, ASMX, JAX-WS, and so on). This way you can model them using your favorite programming language in your favorite editor. So instead of learning WSDL and XML Schema constructs, you can use programming constructs and data types you are already familiar with. The underlying WS stack takes care of the heavy lifting to generate WS contracts in its native form (WSDL and XSD). These declarative programming constructs also makes it a lot easier to put together a new Web service from scratch or expose an existing implementation as a service.

However, this simplicity and convenience could lead to subtle issues if employed without the awareness of the underlying WSDL/XSD constructs used to represent the artifacts you model. Yes, this contradicts the above statement about being able to develop your contracts without the knowledge of WSDL and XSD. But, unfortunately, the truth is the code-first abstraction leaks. Let's try to understand why from a .NET developer point of view.

When you are working with the code, you still have the mindset of modeling object graphs using the .NET type system. For example, the code-first approach does not hinder you from using .NET idiosyncrasies such as System.Data.DataSet or System.DateTimeOffset. These types can definitely be represented in XML (and they are XML-serializable). But a non-.NET client does not have access to the same types.

Another good example is cyclic graphs. As mentioned before, with code-first you still have an object-oriented mindset. Consequently, you tend to model object graphs and those graphs may well have cyclic references. Consider the following data structure with a cyclic reference:

public class OrderItem
    {
        public Order Order { get; set; }
    }

    public class Order
    {
        public List<OrderItem> Items { get; set; }
    }

An Order has a list of OrderItems and each OrderItem has a reference back to the parent Order. If you try to represent this in XML, you hit a problem in that XML does not have a notion of object identity and so you may end up with something similar to the XML in Figure 2, which would go on forever.

Figure 2 Representing an Object Graph with Cyclic References in XML

<Order>
  <Items>
    <OrderItem>
      <Order>
        <Items>
          <OrderItem>
            ...
          </OrderItem>
        </Items>
      </Order>
    </OrderItem>
  </Items>
</Order>

This behavior is due to the difference between the hierarchical nature of XML documents and the graph model in object graphs. Unfortunately, this is something that's not easily detectable when you are following a code-first approach. In order to fix this, you need to enable reference tracking in the DataContractSerializer via IsReference property in DataContractAttribute. But then you will face a different set of problems: first, you would not be able to validate the XML document against a schema using a standard schema validation API; second, the standard reference tracking mechanism used by the serializer is based on section 5 encoding rules defined in SOAP 1.1 specification. These rules are deprecated in document/literal style messages, which is the standard specified in WS-I specifications.

Despite those issues in code-first approach, you may still find it easier to use WCF programming constructs instead of WSDL/XSD constructs as long as you have a little knowledge of how these two worlds are mapped to each other and how WCF serialization works. Although discussing the details of the code-first contract-first approach is beyond the scope of this article, the following diagram should give you a 20,000-foot view of the relationship between the most commonly used WCF contract programming constructs and WSDL/XSD constructs.

Figure 3 Mapping Between Commonly Used Declarative Programming Constructs in WCF and WSDL/Schema Constructs

The main alternative to code-first design is schema-first design, which uses WSDL and XSD directly for your modeling. This is a more natural way of modeling WS contracts as you are now dealing with native constructs. Use of WSDL and XSD gives you the ability to model your contracts with a more XML-centric mindset, eliminating many of the shortcomings in code-first approach.

However, if you want to go down this track, you need to know WSDL and XSD quite well. Also, because these two worlds are connected, you cannot focus completely on WSDL and XSD either. For example, in schema-first approach, at some point you will have to generate code from your schema in order to programmatically manipulate the data structures. Different WS stacks available today provide tools for this (we will talk about the tools available for WCF later in this article). However, due to the enormity of the XML schema specification, these tools often only support a subset of schema constructs. Consequently, if you use these unsupported constructs in your schema, you may still run into problems in terms of interoperability. Also, certain things you model like XSD restrictions have no declarative equivalent in many toolkits and so may disappear during code generation.

In the code-first approach, you write code and let your toolkit generate the WSDL/XSD for you. Consequently, if you change something in your code, it will be automatically reflected in the generated schemas. In the schema-first approach, WSDL and XSD can become stale without a proper change process and disciplined developers in the team. In addition, you may also need the tools to model your WSDLs as your IDE may not have those features out of the box.

Generally, both code-first and schema-first have pros and cons. The schema-first method benefits you particularly in scenarios where you need to use existing schemas, as they may have been modeled during very early stages of your development lifecycle in order to come to an agreement with stakeholders. This is a common situation in government and banking environments. You may also need to use existing schemas if you are building applications complying with existing industry standards such as the Open Travel Alliance (OTA). The choice between the two approaches for contract definition should also be based on your scenario, resources and the skills available in your team. For instance, if you are building a WCF service that doesn't need to support clients running on other platforms, you probably don't want to consider a schema-first approach. Also, if you are quite familiar with contract programming constructs in WCF, have a good understanding of the serialization process, and have the ability to focus on hierarchical structures instead of object graphs, you can follow the code first approach and achieve the same results.

So if you've decided to go with schema-first approach now, the rest of this article shows you how to do it for WCF services.

Schema-First Approach Explained

The schema-first approach has five discrete steps as depicted in Figure 4.


Figure 4 Schema-First Contract-First Development Process

We've already discussed the first three steps at the beginning of this article. Therefore, let's just summarize them and move on to the next few key points.

Step 1 Model data: You model the data structures used to transfer data between your service and clients using XML Schema. You may use the Visual Studio XSD editor or a similar tool like Altova XmlSpy or Liquid XML Studio.

Step 2 Model messages: As we explained earlier, in this step you model the structure of your message body using XML schema. You will probably use the data structures you modeled in Step 1 here.

Step 3 Model interface: In this step, you model your interface contract by defining its operations. However, unlike the earlier steps, here you have to use WSDL instead of XML schema. WSDL editing is not available in Visual Studio but is available in certain products like Altova XmlSpy.

Step 4 Generate code: Once you have modeled your data, messages and operations, it's time to generate code to represent those artifacts in your favorite programming language. The next two sections will elaborate how to do this with tools available today for WCF developers.

Step 5 Iterate contract design and code generation: It's extremely hard to reach perfection in one attempt. Once you design your contract, you may want to refactor it several times until you find the optimal structures you need.

Using Out-of-the-Box WCF Tools for Schema-First Contract-First Development

To demonstrate this scenario, let's build a simple Web service that has an operation to retrieve a list of active processes in the host machine. Starting with the data contract, we model two complex types.

  1. Complex type process represents a process in the host machine.
  2. Complex type processList represents a list of processes defined by process type.

Then we model two elements explore and exploreResponse to abstract the body of input/output messages. As mentioned before, to be compliant with WS-I you have to ensure that your message body has only one child element inside the soap:body element. Both WCF and ASMX handle this by creating a wrapper element with the operation name. If the naming convention used to model messages is different from the WCF standard, code-generation tools will generate MessageContract classes for the request and response. We used the WCF naming convention to name the message elements and thereby have a simpler object model when we generate the code later in this process.

Finally, we model the WSDL using an external tool. In this case, we used Altova XML Spy to help us with that.

Figures 4, 5 and 6 contains the aforementioned data, message and interface contract definitions respectively.

Figure 4 Data.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema" 
           xmlns:ns1="https://schemas.thinktecture.com/contractfirst/2009/07/data" 
           targetNamespace="https://schemas.thinktecture.com/contractfirst/2009/07/data" 
           elementFormDefault="qualified" 
           attributeFormDefault="unqualified"
           >
    <xs:complexType name="process">
        <xs:sequence>
            <xs:element name="pid" type="xs:int"/>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="processList">
        <xs:sequence>
            <xs:element name="process" 
type="ns1:process" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Figure 5 Messages.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema"
           xmlns:data="https://schemas.thinktecture.com/contractfirst/2009/07/data"
           xmlns:ns1="https://schemas.thinktecture.com/contractfirst/2009/07/"
           targetNamespace="https://schemas.thinktecture.com/contractfirst/2009/07/"
           elementFormDefault="qualified">
  <xs:import namespace="https://schemas.thinktecture.com/contractfirst/2009/07/data"
             schemaLocation="data.xsd"/>
  <xs:element name="explore">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="hostname" type="xs:string" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="exploreResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="processes" type="data:processList" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>  
</xs:schema>

Figure 6 ProcessExplorerService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/" 
                  xmlns:soap="https://schemas.xmlsoap.org/wsdl/soap/" 
                  xmlns:http="https://schemas.xmlsoap.org/wsdl/http/" 
                  xmlns:xs="https://www.w3.org/2001/XMLSchema" 
                  xmlns:soapenc="https://schemas.xmlsoap.org/soap/encoding/" 
                  xmlns:mime="https://schemas.xmlsoap.org/wsdl/mime/" 
                  xmlns:tns="https://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:soap12="https://schemas.xmlsoap.org/wsdl/soap12/" 
                  xmlns:msg="https://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:ns="https://schemas.thinktecture.com/contractfirst/2009/07/data"
                  xmlns:wsaw="https://www.w3.org/2006/05/addressing/wsdl"
                  targetNamespace="https://schemas.thinktecture.com/contractfirst/2009/07/">
    <wsdl:types>
        <xs:schema targetNamespace="
https://schemas.thinktecture.com/contractfirst/2009/07/" 
               elementFormDefault="qualified">
            <xs:import schemaLocation="messages.xsd"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="exploreRequestMessage">
        <wsdl:part name="parameters" element="msg:explore"/>    
    </wsdl:message>
    <wsdl:message name="exploreResponseMessage">
        <wsdl:part name="parameters" element="msg:exploreResponse"/>
    </wsdl:message>
    <wsdl:portType name="processExplorer">
        <wsdl:operation name="explore">      
            <wsdl:input wsaw:Action="
https://schemas.thinktecture.com/contractfirst/2009/07/explore" 
                  message="tns:exploreRequestMessage"/>
            <wsdl:output wsaw:Action="
https://schemas.thinktecture.com/contractfirst/2009/07/exploreResponse" 
                   message="tns:exploreResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="processExplorerHttpBinding" type="tns:processExplorer">
        <soap12:binding style="document" transport="
https://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="explore">
            <soap12:operation 
        soapAction="https://schemas.thinktecture.com/contractfirst/2009/07/explore" 
        soapActionRequired="true"/>
            <wsdl:input>        
        <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="ProcessExplorerService">
        <wsdl:port name="processExplorerPort" binding="tns:processExplorerHttpBinding">
            <soap12:address location="https://localhost/processexplorer"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Once the contracts are ready, we can start a Visual Studio 2008 command prompt and generate C# code for WCF using svcutil.exe, as shown in the following code:

svcutil -noconfig -serializer:datacontractserializer -d:../ -
namespace:*,Thinktecture.Samples.ProcessExplorerService
ProcessExplorerService.wsdl Messages.xsd Data.xsd

Svcutil.exe is primarily used to generate code for the client side. Unlike its predecessor wsdl.exe, it does not have an option to generate the service-side skeleton. Consequently, you will notice the generated code contains client-side proxy code, which is not required for the service-side implementation. For the service implementation, you will need to remove these non-relevant types from the generated code. In our sample case, we do that by removing processExplorerClient and processExplorerChannel classes from the generated code. Once you have done that, you can simply implement the generated interface type processExplorer as shown in the ProcessExplorerService1 project in the accompanying sample solution.

Finally, before you host the service, you have to adjust the ServiceMetadataBehavior in order to turn off the automatic WSDL generation and point WCF's metadata generation runtime to the static WSDL file. The following specifies an external metadata location in the WCF configuration:

<serviceMetadata httpGetEnabled="true"                            
externalMetadataLocation="..\contracts\processexplorerservice.wsdl"/>

As you may have noticed, doing schema-first development using this approach could be a little bit overwhelming in real world applications. WSCF.blue, the successor of the free Web service Contract First (WSCF) tool, was created to close the gaps.

A Look at WSCF.blue

WSCF.blue is a free Visual Studio 2008 add-in. Its primary function is enabling schema-first contract-first development inside your favorite IDE. You model your data and message contracts either in Visual Studio XML Schema editor or in a freely available tool like Liquid XML Editor. You may prefer the latter because the schema editor available in Visual studio 2008 provides less modeling support than the one that was available in Visual Studio 2005. Once the schemas are ready you can use WSCF.blue's WSDL generation wizard to model your service interface. It presents a friendly way to model the operations of your interface and hides the details of actual WSDL construction. This wizard can be started by right-clicking on the XSD file containing your message contracts and selecting Create WSDL Interface Description menu item from the context menu, as shown in Figure 7.


Figure 7 Starting the WSDL Generation Wizard

This wizard consists of a few steps to gather information about the operation contracts you wish to have in the service interface. Most of the steps are pretty straightforward. However, there are a few things that may require your attention in Steps 2, 3, and 4.

The types defined in the schema file you selected in Solution Explorer to launch the wizard will be available for WSDL modeling. However, if you have additional types in other XSD files, in Step 2 you can import them as well, as Figure 8 shows.


Figure 8 Importing Additional Schema Documents to the Wizard

In Step 3 you model your operations. The wizard also has a feature that can traverse your message contract elements and infer the operations. This feature, however, only works for messages using the naming conventions used in wizard's inferring rules, as shown in Figure 9.


Figure 9 Add or Remove Operations to the Interface

During Step 4, you can map the messages you modeled into requests and responses of your operations. If the inferring feature in Step 3 has been used, this mapping will be done automatically by the wizard. You can also define message headers in your request and response messages during this step (Figure 10).


Figure 10 Define Input/Output Messages in Operations.

Finally, when you complete the wizard, the generated WSDL file will be added to your project.
Now that the contract is modeled, you can use the WSCF.blue code-generation tool to generate WCF code in your project's language. Right click on the WSDL file in Solution Explorer and select the Generate Web Service Code menu item in the context menu to launch the code-generation tool (Figure 11).


Figure 11 Launching the Code-Generation Tool

The code-generation dialog gives you various code generation options. It contains most of the options available in svcutil.exe as well as some additional ones such as: Server-side stub to generate server-side code; Adjust casing to convert camel case names used in schema to Pascal case names without affecting the serialization; and Separate files to generate a code file for each CLR type generated to represent the corresponding schema artifacts (Figure 12). Figure 13 contains a complete list of options available in the code generation tool.


Figure 12 Code Generation Options

Figure 13 WSCF.blue Code Generation Options

Option Description
Client-side proxy Generates the client-side proxy for service consumers.
Service-side stub Generates the service-side code for service implementers.
Public properties Exposes generated class members as public properties.
Serializable classes Generated classes will be decorated with SerializableAttribute.
Collections Uses Collection<T> type to represent collection types.
List<T> Uses List<T> type to represent collection types.
Data binding Generates the code required to bind generated classes to data bindable components.
Order identifiers Generates Order property for each DataMemberAttribute used.
Async methods Generates the asynchronous version of the operation and sets the OperationContextAttribute.AsyncPattern property to true to tell WCF that they exist.
Separate files Each generated class is placed in its own file.
Adjust casing Uses Pascal case when naming the generated classes and their properties without affecting the serialization process.
Concurrency mode Specifies the value generated for the ServiceBehaviorAttribute.ConcurrencyMode property.
Instance context mode Specifies the value generated for the ServiceBehaviorAttribute.InstanceContextMode property.
Use synchronization context Specifies the value generated for the ServiceBehaviorAttribute.UseSyncrhronizationContext property.
Enable WSDL endpoint Adds an endpoint to the service to expose static metadata files. This is essential when you want to expose static metadata documents in self hosted services.
Destination file name Name of the output file. If Separate files option is turned on, this parameter is discarded and class names are used as filenames.
Destination namespace CLR namespace to be used for the generated types.
Remember settings Remembers the most recently used code generation options.
Overwrite existing files Specifies that you allow WSCF.blue to overwrite existing files instead of creating unique filenames to resolve the conflicts. It’s important to keep in mind that any customizations you’ve done to the generated code will be overwritten. Therefore we recommend you to do such modifications via partial classes.

The release of WSCF.blue discussed in this article does not yet support merging configuration files. Consequently, the required configuration to host your service is emitted to an output.config file. A manual step is required to move the content of this file to your web.config or app.config file as appropriate.

Once the configuration file is ready, you can continue to add the necessary implementation to the service class generated.

Finally, you can modify your schemas in Visual Studio and use WSDL round-tripping features in the WSDL generation wizard to change your contracts and regenerate the code.

What’s Next

WSCF.blue is released as an open source project and available at wscfblue.codeplex.com. The current release has some limitations that may restrict you to the basic but most common Web service implementations. For example, the WSDL generation wizard currently supports only basic http binding. Also, the current code generation in WSCF.blue only generates XmlSerializable classes instead of DataContractSerializable classes to support wider range of schema constructs. A complete list of known issues and a road map of the tool is available at the CodePlex site.

Conclusion

Schema- based contract-first modeling of Web Services gives you the ability to model your contracts with an XML-centric mindset. This process keeps you focused on universally acceptable types and the hierarchical data structures that can be represented in XML. Tooling support is essential to making a Web Services platform successful. WSCF.blue extends the out of the box tooling provided by WCF to make schema-based contract-first development easier for WCF developers.


Christian Weyer is cofounder and principal architect of thinktecture and has been modeling and implementing distributed applications with Java, COM, DCOM, COM+, Web Services, WCF, and other technologies for many years. Christian is the original inventor of the classic Web Services Contract First tool for .NET. Get in touch with him at https://weblogs.thinktecture.com/cweyer/.

Buddhike de Silva is an engineering fellow based in Australia. Before moving to Australia, he was the lead engineer at thinktecture. Buddhike has been designing and implementing distributed applications for many years and he is also a long time contributor to WSCF/WSCF.blue projects.


1Design by Contract, Technical Report TR-EI-12/CO, Interactive Software Engineering Inc., 1986.

2https://se.ethz.ch/~meyer/publications/computer/contract.pdf

3RESTful services however communicate with their clients by exchanging standard HTTP messages and follow a totally different paradigm.