Friday, September 24, 2010

WSDL-First development with Visual Studio

This is how I implemented the WSDL-First approach when creating a WCF service with Visual Studio.

An example of a simple WSDL:

<?xml version="1.0" encoding="utf-8"?>
<definitions
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://www.myweb.com/ws/"
  xmlns:s="http://www.w3.org/2001/XMLSchema"
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
  targetNamespace="http://www.myweb.com/ws/"
  xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://www.myweb.com/ws/">
      <s:element name="DoSomething">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="param1" type="s:string" />
            <s:element minOccurs="0" maxOccurs="1" name="param2" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="DoSomethingResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="DoSomethingResult" type="tns:TheDTO" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="TheDTO">
        <s:complexContent mixed="false">
          <s:extension base="tns:BaseDTO">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="Att1" type="s:string" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="BaseDTO" abstract="true">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="BaseAtt1" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="BaseAtt2" type="s:string" />
        </s:sequence>
      </s:complexType>
    </s:schema>
  </types>
  <message name="DoSomethingSoapIn">
    <part name="parameters" element="tns:DoSomething" />
  </message>
  <message name="DoSomethingSoapOut">
    <part name="parameters" element="tns:DoSomethingResponse" />
  </message>
  <portType name="IService">
    <operation name="DoSomething">
      <documentation xmlns="http://schemas.xmlsoap.org/wsdl/">Do something.</documentation>
      <input message="tns:DoSomethingSoapIn" />
      <output message="tns:DoSomethingSoapOut" />
    </operation>
  </portType>
  <binding name="TheServiceSoap" type="tns:IService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <operation name="DoSomething">
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
  <service name="TheService">
    <port binding="tns:TheServiceSoap" name="TheServicePort"/>
  </service>
</definitions>

Tip: It can be useful to download a WSDL-template to start with, instead of writing it from scratch.
Use the svcutil utility to save the WSDL-file exported from a service:

> svcutil /t:metadata http://localhost:8731/TheService/Service/

I added a Pre-Build command that reads my WSDL-file and creates an interface file in preferred language, in my case C#:

svcutil /language:C# /n:*,TheService /out:$(ProjectDir)IService.cs $(ProjectDir)\TheService.wsdl

The IService.cs will be regenerated each time I initiates a build so I adds the implementation of the interface in a separate file, Service.cs.

using System;
namespace TheService
{
    using System.ServiceModel;

    [ServiceBehavior(Namespace = "http://www.myweb.com/ws/")]
    public class Service : IService
    {
        public TheDTO DoSomething(string param1, string param2)
        {
            throw new NotImplementedException();
        }
    }
}

It is preferable to store the WSDL file separately in the VCS, then you can redesign both server and clients without affecting the functionality, as long as they use the same version of the WSDL file.

 

The WSDL can now be used to create service clients.
For example, to create a .NET client, use the svcutil utility to create the service proxy code:

> svcutil TheService.wsdl

The svcutil utility creates a Service.cs that you include in the client project, and then the service can be invoked as if it were a local object.

var myService = new ServiceClient();

TheDTO result = myService.DoSomething("A", "B");