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

1 comment:

  1. Hi Markus,

    Thank you for sharing this. It's very helpful.

    I have a question for you if you can help. Instead of code, we only have XSD files as input. And we have to generate WSDL file programmatically based on those XSD files and fixed method in WCF service that looks as follows:

    OutputMessage CustomAction(InputMessage) {...}

    InputMessage should be body of SOAP message request and OutputMessage should be body of SOAP message response of WCF service that is running.

    WSDL file should be generated on the fly as XML string and passed further to help running WCF service.

    What do you think how can we do that?

    Thank you in advance.

    Goran

    ReplyDelete