Custom Namespaces

If you've ever looked at a generated WSDL file, you may be wondering how all of the different parts of the service description are reassembled to form WSDL. Today's article is about the namespaces in the WSDL file. Here's an example service that provides a unique custom namespace to each of the most common parts of the service description.

 using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Runtime.Serialization;

[DataContract(Namespace = "example.com/faultcontract/datacontract")]
public class MyFault
{
   [DataMember]
   public string detail;
}

[DataContract(Namespace = "example.com/datacontract")]
public class MyData
{
   [DataMember]
   public string data;
}

[MessageContract(IsWrapped = true, WrapperNamespace = "example.com/messagecontract")]
public class MyMessage
{
   [MessageHeader(Namespace = "example.com/messageheader")]
   public string header;

   [MessageBodyMember(Namespace = "example.com/messagebodymember")]
   public string member;
}

[ServiceContract(Namespace = "example.com/servicecontract")]
public interface IService
{
   [FaultContract(typeof(MyFault), Namespace = "example.com/faultcontract/operationcontract")]
   [OperationContract]
   void Operation1(MyData data);

   [OperationContract]
   void Operation2(MyMessage message);
}

[ServiceBehavior(Namespace="example.com/servicebehavior")]
public class Service : IService
{
   public void Operation1(MyData data)
   {
   }

   public void Operation2(MyMessage message)
   {
   }
}

class Program
{
   static void Main(string[] args)
   {
      ServiceHost host = new ServiceHost(typeof(Service), new Uri("localhost:8000/"));
      Binding binding = new CustomBinding(new TextMessageEncodingBindingElement(), new HttpTransportBindingElement());
      binding.Namespace = "example.com/binding";
      host.AddServiceEndpoint(typeof(IService), binding, "");
      host.Description.Behaviors.Add(new ServiceMetadataBehavior());
      host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
      host.Open();
      Console.ReadLine();
      host.Close();
   }
}

You can run svcutil against this service to generate all of the WSDL and schema files. Now, we have a way to work backwards from the elements in the WSDL file to find where they are defined in the service description. Running svcutil gives me three WSDL files and seven schema files. One of the schema files covers the standard serialization types that are included with every service. Everything else is customized by my custom namespaces.

To start the trace through the service description, let's look at example.com.servicebehavior.wsdl.

 <?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="example.com/binding" xmlns:wsa="schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="www.w3.org/2001/XMLSchema" xmlns:msc="schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="example.com/servicebehavior" xmlns:wsaw="www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="www.w3.org/2005/08/addressing" name="Service" targetNamespace="example.com/servicebehavior" xmlns:wsdl="schemas.xmlsoap.org/wsdl/">
  <wsdl:import namespace="example.com/binding" location="" />
  <wsdl:types />
  <wsdl:service name="Service">
    <wsdl:port name="CustomBinding_IService" binding="i0:CustomBinding_IService">
      <soap12:address location="localhost:8000/" />
      <wsa10:EndpointReference>
        <wsa10:Address>localhost:8000/</wsa10:Address>
      </wsa10:EndpointReference>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

This gives a reference to the service endpoint itself and links us to the example.com.binding.wsdl file.

 <?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="example.com/servicecontract" xmlns:wsa="schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="www.w3.org/2001/XMLSchema" xmlns:msc="schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="example.com/binding" xmlns:wsaw="www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="www.w3.org/2005/08/addressing" targetNamespace="example.com/binding" xmlns:wsdl="schemas.xmlsoap.org/wsdl/">
  <wsp:Policy wsu:Id="CustomBinding_IService_policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <wsaw:UsingAddressing />
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>
  <wsdl:import namespace="example.com/servicecontract" location="" />
  <wsdl:types />
  <wsdl:binding name="CustomBinding_IService" type="i0:IService">
    <wsp:PolicyReference URI="#CustomBinding_IService_policy" />
    <soap12:binding transport="schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="Operation1">
      <soap12:operation soapAction="example.com/servicecontract/IService/Operation1" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
      <wsdl:fault name="MyFaultFault">
        <soap12:fault use="literal" name="MyFaultFault" namespace="" />
      </wsdl:fault>
    </wsdl:operation>
    <wsdl:operation name="Operation2">
      <soap12:operation soapAction="example.com/servicecontract/IService/Operation2" style="document" />
      <wsdl:input name="MyMessage">
        <soap12:header message="i0:MyMessage_Headers" part="header" use="literal" />
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
</wsdl:definitions>

Now, we have a skeleton for the service operations but are referenced to example.com.servicecontract.wsdl to find descriptions of the messages.

 <?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="www.w3.org/2001/XMLSchema" xmlns:msc="schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="example.com/servicecontract" xmlns:wsaw="www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="www.w3.org/2005/08/addressing" targetNamespace="example.com/servicecontract" xmlns:wsdl="schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <xsd:schema targetNamespace="example.com/servicecontract/Imports">
      <xsd:import namespace="example.com/servicecontract" />
      <xsd:import namespace="example.com/faultcontract/datacontract" />
      <xsd:import namespace="schemas.microsoft.com/2003/10/Serialization/" />
      <xsd:import namespace="example.com/datacontract" />
      <xsd:import namespace="example.com/messagecontract" />
      <xsd:import namespace="example.com/messageheader" />
      <xsd:import namespace="example.com/messagebodymember" />
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="IService_Operation1_InputMessage">
    <wsdl:part name="parameters" element="tns:Operation1" />
  </wsdl:message>
  <wsdl:message name="IService_Operation1_OutputMessage">
    <wsdl:part name="parameters" element="tns:Operation1Response" />
  </wsdl:message>
  <wsdl:message name="IService_Operation1_MyFaultFault_FaultMessage">
    <wsdl:part xmlns:q1="example.com/faultcontract/datacontract" name="detail" element="q1:MyFault" />
  </wsdl:message>
  <wsdl:message name="MyMessage">
    <wsdl:part xmlns:q2="example.com/messagecontract" name="parameters" element="q2:MyMessage" />
  </wsdl:message>
  <wsdl:message name="MyMessage_Headers">
    <wsdl:part xmlns:q3="example.com/messageheader" name="header" element="q3:header" />
  </wsdl:message>
  <wsdl:message name="IService_Operation2_OutputMessage" />
  <wsdl:portType name="IService">
    <wsdl:operation name="Operation1">
      <wsdl:input wsaw:Action="example.com/servicecontract/IService/Operation1" message="tns:IService_Operation1_InputMessage" />
      <wsdl:output wsaw:Action="example.com/servicecontract/IService/Operation1Response" message="tns:IService_Operation1_OutputMessage" />
      <wsdl:fault wsaw:Action="example.com/servicecontract/IService/Operation1MyFaultFault" name="MyFaultFault" message="tns:IService_Operation1_MyFaultFault_FaultMessage" />
    </wsdl:operation>
    <wsdl:operation name="Operation2">
      <wsdl:input wsaw:Action="example.com/servicecontract/IService/Operation2" name="MyMessage" message="tns:MyMessage" />
      <wsdl:output wsaw:Action="example.com/servicecontract/IService/Operation2Response" message="tns:IService_Operation2_OutputMessage" />
    </wsdl:operation>
  </wsdl:portType>
</wsdl:definitions>

Finally, we have no more WSDL references and these message descriptions link off to each of the individual schema files, which you can go through yourself. For any particular namespace, you can automatically link back to where in the service it was defined. If you've been wondering how to customize a particular namespace in the WSDL or schema, then you can use this example to trace back where the namespace is supposed to go.

Next time: Increasing the Maximum Fault Size