WCF and Custom Bindings

In this post, I am going to create a custom binding on the service side that does the same exact thing as the basicHttpBinding does, and then we'll call that service from a client configured to use the basicHttpBinding on the client to prove it works.  In my previous blog post on WCF bindings, What's In a WCF Binding, And What Can My Service Do Right Now?, I mentioned how you could create your own custom version of basicHttpBinding.  I got a few emails asking about problems in trying to make this work, I thought I would clarify how to do this.

First, let's code our service.  We'll use a slimmed version of the code generated by the "New / WCF Service Library" template in Visual Studio 2008.

 using System;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace WcfServiceLibrary1
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);        
    }

    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

    public class Service1 : IService1
    {
        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}

Let me point out that there is absolutely nothing in our code that says where we are going (the address) or how we are going to get there (the binding).  This is simply the codified version of what we are going to say to each other in our distributed conversation (the contract).  In other words, we specify the data to be transmitted over the wire, but we never say how it will be transmitted (encoded as text, binary, JSON, or other) and where it will go (over HTTP, TCP, a queue, a carrier pigeon's foot, etc).  That's what our configuration file achieves, and this is one of the things I love about WCF.  The developers focus on what the service is supposed to achieve, and the operations / IT pro folks can focus on the infrastructure stuff.

Now take a look at how we can configure the service to use a custom binding. I err on the side of verbosity here, including the entire configuration file for easier understanding... this is how you configure the service.

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="myBinding">
          <textMessageEncoding messageVersion="Soap11" />
          <httpTransport />
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
        name="WcfServiceLibrary1.Service1">
        <endpoint 
          address="" 
          binding="customBinding" 
          bindingConfiguration="myBinding"
          contract="WcfServiceLibrary1.IService1">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WcfServiceLibrary1.Service1Behavior">
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Yeah, it's verbose, and my eyes are burning, too, just looking at all that XML.  The part you should really focus on is the definition of "myBinding" which includes the textMessageEncoding and httpTransport elements.  Notice that the textMessageEncoding element specifies the messageVersion attribute "Soap11".  There are multiple possible values for the messageVerision attribute:

  • None - No SOAP, just Plan Old XML.
  • Soap11 - Uses SOAP 1.1.
  • Soap12 - Uses SOAP 1.2.
  • Soap11WSAddressing10 - Uses SOAP 1.1 and W3C Recommendation WS-Addressing 1.0 - Core
  • Soap12WSAddressing10 - Uses SOAP 1.2 and W3C Recommendation WS-Addressing 1.0 - Core
  • Soap11WSAddressingAugust2004 - Uses SOAP 1.1 and the legacy W3C submission for WS-Addressing.  This is the same version of WS-Addressing that WSE 3.0 uses, and is included for wire-level interop between WSE 3.0 and WCF.
  • Soap12WSAddressingAugust2004 - Uses SOAP 1.1 and the legacy W3C submission for WS-Addressing.  This is the same version of WS-Addressing that WSE 3.0 uses, and is included for wire-level interop between WSE 3.0 and WCF.

We are going to talk using basicHttpBinding which, by default, uses text encoding with SOAP 1.1 over HTTP.

Last, we need to configure the client.  We can just use "Add Service Reference" in Visual Studio 2008, or use svcutil.exe and generate the client config ourselves.  Or, even easier, we can write the client config ourselves, and it will be much more compact than what VS2008 or svcutil generates.

 <?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <bindings />
        <client>
            <endpoint 
                address="https://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/"
                binding="basicHttpBinding" 
                contract="IService1"
                name="CustomBinding_IService1" />
        </client>
    </system.serviceModel>
</configuration>

Nice and clean... we can simply use basicHttpBinding on the client because it does the exact same thing that our config on the service does... it talks SOAP 1.1 over HTTP using a text encoding.  In fact, we could replace our service's config with the following and it would not change behavior at all (because our custom binding is equivalent to the basicHttpBinding). 

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
        name="WcfServiceLibrary1.Service1">
        <endpoint 
          address="" 
          binding="basicHttpBinding" 
          contract="WcfServiceLibrary1.IService1">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WcfServiceLibrary1.Service1Behavior">
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>