What's In a WCF Binding, And What Can My Service Do Right Now?

I saw an interesting question in an internal mailing list, asking how to show the binding name for an endpoint.  The real purpose for the question was to demo how, using WAS, you can host a service with multiple endpoints and still get the IIS activation goodness.  It turns out that this is really, really easy to do in WCF.

A WCF service can listen on multiple endpoints.  Each endpoint has 3 basic properties, the ABC's of WCF:  Address, Binding, and Contract.  To greatly simplify this, Address says where you are going, Contract says what you are going to say, and Binding says how you are going to get there.  It's that last part that is deceptively simplified, because there is a bit of stuff contained in a Binding.

A Binding is really the grouping of BindingElements.  Each BindingElement represents individual capabilities that define the overall shape of the message exchange.  For instance, the most common WCF endpoint, in config, looks like this:

 <endpoint
   address="https://localhost:8080/myservice"
   binding="basicHttpBinding"
   contract="IService1" />

The basicHttpBinding indicates that your service can "talk" over HTTP and exchange text.  It doesn't get all of the WS-* goodness, like WS-Addressing, WS-Security, or WS-ReliableMessaging, it just talks text over HTTP according to the WS-I Basic Profile 1.1.  This is basically the same thing that ASMX web services "speak": they can only talk HTTP with text.

Because these details (text and HTTP) is abstracted away from your code, your service is blissfully ignorant of whether it is going to use HTTP, TCP, MSMQ, or some other protocol.  Your service doesn't care if messages are sent using JSON, SOAP, POX, binary, or some other format.  In fact, the code that you express as part of your service has no carnal knowledge of any of these details, it only knows about the CLR types that you ultimately want to work with.

     public interface IService1
    {
        [OperationContract]
        string GetData(string simple);
    }

There may come a time when you want to know what your service can actually do, what facilities it can leverage.  You may have cause to want to know things like the Binding name.  That's pretty easy to inspect in the service side of things.

         public string GetData(string simple)
        {
              string ret = "";
            foreach (var ep in OperationContext.Current.Host.Description.Endpoints)
            {
                 ret += ep.Binding.Name + " ");
            }            
            return ret;            
        }
    }

By default, you know that basicHttpBinding can do text over HTTP, and the netTcpBinding can talk binary over TCP.  This is interesting until you venture outside the pre-defined bindings and start to create your own bindings.  For instance, what if I alter the binding configuration for one of the bindings out of the box, like this?

       <wsHttpBinding>
        <binding name="basicConfiguration">
          <reliableSession enabled="false"/>
          <security mode="None"/>          
        </binding>
      </wsHttpBinding>

When I ask for the Binding.Name, I will get "wsHttpBinding".  That doesn't let me know that security is turned off, or that reliable sessions are disabled.  After these configuration changes, the wsHttpBinding will act and look just like our custom binding and the basicHttpBinding, despite having the "ws" name in there.  So, the Binding.Name property doesn't tell the whole story, what tells the story is the capabilities that are configured for that binding. 

Take it a step further, and create our own custom binding.

         <customBinding>
          <binding name="MyHttpBinding">
            <textMessageEncoding />
            <httpTransport />
          </binding>
        </customBinding>

When I ask for the Binding.Name, the above code would simply return "CustomBinding".  That doesn't tell us what our custom binding is currently configured to allow.  Remember that the config is in the hands of the operations people once it is deployed:  they may change some of these characteristics according to their platform infrastructure.  They might decide that they want to expose an endpoint that enables binary communication over HTTP to reduce bandwidth consumption.

         <customBinding>
          <binding name="MyBinaryHttpBinding">
            <binaryMessageEncoding />
            <httpTransport />
          </binding>
        </customBinding>

Before I go further, you might be wondering how I used each of the above binding configurations.  The complete config file looks like this:

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="basicConfiguration">
          <reliableSession enabled="false"/>
          <security mode="None"/>          
        </binding>
      </wsHttpBinding>
      <customBinding>
        <binding name="MyHttpBinding" >
          <textMessageEncoding />
          <httpTransport/>                    
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
        name="WcfServiceLibrary1.Service1">
        <endpoint address="" binding="basicHttpBinding" contract="WcfServiceLibrary1.IService1">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <endpoint address="secure" binding="wsHttpBinding" bindingConfiguration="basicConfiguration"
          contract="WcfServiceLibrary1.IService1" />
        <endpoint address="custom" binding="customBinding" bindingConfiguration="MyHttpBinding"
  contract="WcfServiceLibrary1.IService1" />
        <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"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

That's another cool thing about WCF... I can host the same service with multiple endpoints, each endpoint providing different capabilities depending on how it is called.  That separation is provided through the Binding concept.  Remember how a Binding is really just a grouping of capabilities, like text over HTTP?  Each one of those capabilities is represented in a BindingElement.  You can inspect those capabilities programmatically, providing much more insight into your service'scurrently configured  capabilities.

             foreach (var ep in OperationContext.Current.Host.Description.Endpoints)
            {
                 System.Diagnostics.Debug.WriteLine(ep.Binding.Name);
                 foreach (var elem in ep.Binding.CreateBindingElements())
                 {
                     System.Diagnostics.Debug.WriteLine("\t" + elem.GetType().Name);
                 }
            }

Now, we can inspect not only the name of the binding, but how it is currently configured as well. 

 BasicHttpBinding
    TextMessageEncodingBindingElement
    HttpTransportBindingElement
MetadataExchangeHttpBinding
    TransactionFlowBindingElement
    TextMessageEncodingBindingElement
    HttpTransportBindingElement
WSHttpBinding
    TransactionFlowBindingElement
    TextMessageEncodingBindingElement
    HttpTransportBindingElement
CustomBinding
    TextMessageEncodingBindingElement
    HttpTransportBindingElement

I am compelled to point out one more thing... I stated earlier that our reconfigured wsHttpBinding looked and acted just like the basicHttpBinding.  That's not entirely true, due to the "transactionFlowBindingElement" above.  What that says is, by using this particular binding, it is possible to flow a transaction context using WS-AtomicTransactions.  That's not something that our CustomBinding or the BasicHttpBinding are capable of, and it's not something you can configure away for the wsHttpBinding. 

Hopefully now you have a little more insight into bindings in WCF and how to configure them.  Go tinker with the configuration file and look at the different capabilities you can layer in.  The Intellisense in Visual Studio 2008 will help you out, as it understands which child elements are allowed for a particular binding type.

For more information on WCF Bindings, here are some great resources.