May 18, 2008

Creating a WSDL using C#

Have you ever thought about generating a WSDL file? For use in a DSL to create a web service description file directly?

Version one I found out was

ContractDescription cd = ContractDescription.GetContract(typeof(IContract));

System.ServiceModel.Description.WsdlExporter wexp = new System.ServiceModel.Description.WsdlExporter();

wexp.ExportContract(cd);

MetadataSet mds = wexp.GetGeneratedMetadata();

mds.WriteTo(new XmlTextWriter(@".\IContract.wsdl", Encoding.UTF8));



for a simple contract like

namespace WsdlExporter

{

    [MessageContract]

    public class RequestMessage

    {

        public Guid ID;

    }

 

    [MessageContract]

    public class ResponseMessage

    {

        public string Name;

    }

 

    [ServiceContract(Namespace="http://www.mleder.blogspot.com/WsdlExporter/2008/04")]

    public interface IContract

    {

        [OperationContract]

        void Op1(string s);

 

        [OperationContract]

        int Op2();

 

        [OperationContract]

        ResponseMessage Op3(RequestMessage req);

    }

}



I think this is verbose.


So I figured out version two, where I start with an "empty" WSDL file:

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions xmlns:tns="http://localhost/remote"

                  xmlns:s="http://www.w3.org/2001/XMLSchema"

                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"

                  targetNamespace="http://localhost/remote"

                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <wsdl:types>

    <s:schema elementFormDefault="qualified" targetNamespace="http://localhost/remote">

    </s:schema>

  </wsdl:types>

  <wsdl:portType name="RemoteAddServiceSoap12">

  </wsdl:portType>

  <wsdl:binding name="RemoteAddServiceSoap12" type="tns:RemoteAddServiceSoap12">

    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />

  </wsdl:binding>

  <wsdl:service name="RemoteAddService">

    <wsdl:port name="RemoteAddServiceSoap12" binding="tns:RemoteAddServiceSoap12">

      <soap12:address location="http://localhost:8765/RemoteAddService.asmx" />

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>



and write some C# code:

// load an "empty" WSDL file (no types, messages, operations, ...)

// to start from and add operations by code

ServiceDescription wsdl = ServiceDescription.Read("empty.wsdl");

 

// add operation binding

OperationBinding myOperationBinding = new OperationBinding();

myOperationBinding.Name = "Add";

Soap12OperationBinding myOperation = new Soap12OperationBinding();

myOperation.Style = SoapBindingStyle.Document;

myOperation.SoapAction = "http://localhost/remote/Add";

myOperationBinding.Extensions.Add(myOperation);

 

// add input binding

InputBinding myInput = new InputBinding();

Soap12BodyBinding soap12BodyInputBinding = new Soap12BodyBinding();

soap12BodyInputBinding.Use = SoapBindingUse.Literal;

myInput.Extensions.Add(soap12BodyInputBinding);

myOperationBinding.Input = myInput;

 

// add output binding

OutputBinding myOutput = new OutputBinding();

Soap12BodyBinding soap12BodyOutputBinding = new Soap12BodyBinding();

soap12BodyOutputBinding.Use = SoapBindingUse.Literal;

myOutput.Extensions.Add(soap12BodyOutputBinding);

myOperationBinding.Output = myOutput;

wsdl.Bindings[0].Operations.Add(myOperationBinding);

 

// 1. ==> add operation to port type

Operation operation = new Operation();

operation.Name = "Add";

OperationMessage outputMessage = (OperationMessage)new OperationOutput();

outputMessage.Message = new XmlQualifiedName("tns:AddOutput");

OperationInput inputMessage = new OperationInput();

inputMessage.Message = new XmlQualifiedName("tns:AddInput");

operation.Messages.Add(inputMessage);

operation.Messages.Add(outputMessage);

wsdl.PortTypes[0].Operations.Add(operation);

 

// add first message

Message message1 = new Message();

message1.Name = "AddInput";

MessagePart messagePart1 = new MessagePart();

messagePart1.Name = "parameter1";

messagePart1.Type = new XmlQualifiedName("s:int");

MessagePart messagePart2 = new MessagePart();

messagePart2.Name = "parameter2";

messagePart2.Type = new XmlQualifiedName("s:int");

message1.Parts.Add(messagePart1);

message1.Parts.Add(messagePart2);

 

// add second message

Message message2 = new Message();

message2.Name = "AddOutput";

 

// add third message

MessagePart messagePart3 = new MessagePart();

messagePart3.Name = "result";

messagePart3.Type = new XmlQualifiedName("s:int");

message2.Parts.Add(messagePart3);

 

// 2. ==> add messages

wsdl.Messages.Add(message1);

wsdl.Messages.Add(message2);

 

// write the service description into a WSDL file.

wsdl.Write("empty_add.wsdl");

Console.WriteLine("WSDL file named empty_add.wsdl created successfully.");



Using version two, you have full control over all types, messages, operations, bindings, etc. and get a short and easy:

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions xmlns:tns="http://localhost/remote" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://localhost/remote" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <wsdl:types>

    <s:schema elementFormDefault="qualified" targetNamespace="http://localhost/remote" />

  </wsdl:types>

  <wsdl:message name="AddInput">

    <wsdl:part name="parameter1" type="s:int" />

    <wsdl:part name="parameter2" type="s:int" />

  </wsdl:message>

  <wsdl:message name="AddOutput">

    <wsdl:part name="result" type="s:int" />

  </wsdl:message>

  <wsdl:portType name="RemoteAddServiceSoap12">

    <wsdl:operation name="Add">

      <wsdl:input message="tns:AddInput" />

      <wsdl:output message="tns:AddOutput" />

    </wsdl:operation>

  </wsdl:portType>

  <wsdl:binding name="RemoteAddServiceSoap12" type="tns:RemoteAddServiceSoap12">

    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />

    <wsdl:operation name="Add">

      <soap12:operation soapAction="http://localhost/remote/Add" style="document" />

      <wsdl:input>

        <soap12:body use="literal" />

      </wsdl:input>

      <wsdl:output>

        <soap12:body use="literal" />

      </wsdl:output>

    </wsdl:operation>

  </wsdl:binding>

  <wsdl:service name="RemoteAddService">

    <wsdl:port name="RemoteAddServiceSoap12" binding="tns:RemoteAddServiceSoap12">

      <soap12:address location="http://localhost:8765/RemoteAddService.asmx" />

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>



To create a WCF proxy for C# :
svcutil -n:*,srRemoteAddService empty_add.wsdl