WCF and Bindings

In this third article in a five-part series on using the Windows Communication Foundation (WCF), you will learn how to choose and configure a binding, and more. It is excerpted from chapter one of Programming WCF Services, written by Juval Lowry (O’Reilly, 2007; ISBN: 0596526997). Copyright © 2007 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

Choosing a Binding

Choosing a binding for your service should follow the decision-activity diagram shown in Figure 1-4.

The first question you should ask yourself is whether your service needs to interact with non-WCF clients. If the answer is yes, and if the client is a legacy MSMQ client, choose the MsmqIntegrationBinding that enables your service to interoperate over MSMQ with such a client. If you need to interoperate with a non-WCF client and that client expects basic web service protocol (ASMX web services), choose the BasicHttpBinding , which exposes your WCF service to the outside world as if it were an ASMX web service (that is, a WSI-basic profile). The downside is that you cannot take advantage of most of the modern WS-* protocols. However, if the non-WCF client can understand these standards, choose one of the WS bindings, such as WSHttpBindingWSFederationBinding , or WSDualHttpBinding . If you can assume that


Figure 1-4.  Choosing a binding

the client is a WCF client, yet it requires offline or disconnected interaction, choose the NetMsmqBinding that uses MSMQ for transporting the messages. If the client requires connected communication, but could be calling across machine boundaries, choose the NetTcpBinding that communicates over TCP. If the client is on the same machine as the service, choose the NetNamedPipeBinding that uses named pipes to maximize performance. You may fine-tune binding selections based on additional criteria such as the need for callbacks ( WSDualHttpBinding ) or federated security ( WSFederationBinding ).

Most bindings work well even outside their target scenario. For example, you could use the TCP binding for same-machine or even in-proc communication, and you could use the basic binding for Intranet WCF-to-WCF communication. However, do try to choose a binding according to Figure 1-4.

Using a Binding

Each binding offers literally dozens of configurable properties. There are three modes of working with bindings. You can use the built-in bindings as is if they fit your requirements. You can tweak and configure some of their properties such as transaction propagation, reliability, and security. You can also write your own custom bindings. The most common scenario is using an existing binding almost as is, and merely configuring two or three of its aspects. Application developers will hardly ever need to write a custom binding, but framework developers may need to.

{mospagebreak title=Endpoints} 

Every service is associated with an address that defines where the service is, a binding that defines how to communicate with the service, and a contract that defines what the service does. This triumvirate governing the service is easy to remember as the ABC of the service. WCF formalizes this relationship in the form of an endpoint. The endpoint is the fusion of the address, contract, and binding (see Figure 1-5).


Figure 1-5.  The endpoint

Every endpoint must have all three elements, and the host exposes the endpoint. Logically, the endpoint is the service’s interface, and is analogous to a CLR or COM interface. Note in Figure 1-5 the use of the traditional “lollipop” to denote an endpoint.

Conceptually, even in C# or VB, an interface is an endpoint: the address is the memory address of the type’s virtual table, the binding is CLR JIT compiling, and the contract is the interface itself. Because in classic .NET programming you never deal with addresses or bindings, you take them for granted. In WCF the address and the binding are not ordained, and need to be configured.

Every service must expose at least one business endpoint and each endpoint has exactly one contract. All endpoints on a service have unique addresses, and a single service can expose multiple endpoints. These endpoints can use the same or different bindings and can expose the same or different contracts. There is absolutely no relationship between the various endpoints a service provides.

It is important to point out that nothing in the service code pertains to its endpoints and they are always external to the service code. You can configure endpoints either administratively using a config file or programmatically.

Administrative Endpoint Configuration

Configuring an endpoint administratively requires placing the endpoints in the hosting process’ config file. For example, given this service definition:

  namespace MyNamespac e
  {
    
[ServiceContract]
     interface IMyContract
     {…}
     class MyService : IMyContract
     {…}
 
}

Example 1-6 shows the required entries in the config file. Under each service type you list its endpoints.

Example 1-6. Administrative endpoint configuration

<system.serviceModel>
   <services>
      <service name = "MyNamespace.MyService">
        
<endpoint
            address  = http://localhost:8000/MyService/
            binding  = "wsHttpBinding" 
            contract = "MyNamespace.IMyContract"
         />
      </service>
   </services>
</system.serviceModel>

When you specify the service and the contract type, you need to use fully qualified type names. I will omit the namespace in the examples throughout the remainder of this book, but you should use a namespace when applicable. Note that if the end point provides a base address, then that address schema must be consistent with the binding, such as HTTP with WSHttpBinding . A mismatch causes an exception at the service load time.

Example 1-7 shows a config file defining a single service that exposes multiple endpoints. You can configure multiple endpoints with the same base address as long as the URI is different.

Example 1-7. Multiple endpoints on the same service

<service name  = "MyService">
  
<endpoint
       address  = http://localhost:8000/MyService/
       binding  = "wsHttpBinding"
       contract = "IMyContract"
  
/>
   <endpoint
       address  = "net.tcp://localhost:8001/MyService/"
       binding  = "netTcpBinding"
       contract = "IMyContract"
  
/>
   <endpoint
       address  = "net.tcp://localhost:8002/MyService/"
       binding  = "netTcpBinding"
       contract = "IMyOtherContract"
  
/>
</service>

Administrative configuration is the option of choice in the majority of cases because it provides the flexibility to change the service address, binding, and even exposed contracts without rebuilding and redeploying the service.

Using base addresses

In Example 1-7, each endpoint provided its own base address. When you provide an explicit base address, it overrides any base address the host may have provided.

You can also have multiple endpoints use the same base address, as long as the end point addresses differ in their URIs:

  <service name  = "MyService">
     <endpoint
        address  = "net.tcp://localhost:8001/MyService/"
        binding  = "netTcpBinding"
        contract = "IMyContract"
     />
    
<endpoint
        address  = "net.tcp://localhost:8001/MyOtherService/"
        binding  = "netTcpBinding"
        contract = "IMyContract"
     />
  </service>

Alternatively, if the host provides a base address with a matching transport schema, you can leave the address out, in which case the endpoint address will be the same as the base address of the matching transport:

  <endpoint
     binding  = "wsHttpBinding"
     contract = "IMyContract"
 
/>

If the host does not provide a matching base address, loading the service host will fail with an exception.

When you configure the endpoint address you can add just the relative URI under the base address:

  <endpoint
     address  = "SubAddress"
     binding  = "wsHttpBinding"
     contract = "IMyContract"
 
/>

The endpoint address in this case will be the matching base address plus the URI, and, again, the host must provide a matching base address.

{mospagebreak title=Binding configuration}

You can use the config file to customize the binding used by the endpoint. To that end, add the bindingConfiguration tag to the endpoint section, and name a custom ized section in the bindings section of the config file. Example 1-8 demonstrates using this technique to enable transaction propagation. What the transactionFlow tag does will be explained in Chapter 7.

Example 1-8. Service-side binding configuration

<system.serviceModel>
   <services>
      <service name = "MyService"> 
         <endpoint
            address  = "net.tcp://localhost:8000/MyService/"
           
bindingConfiguration = "TransactionalTCP"
          
binding  = "netTcpBinding"
            
contract = "IMyContract "
         />
         <endpoint
            address  = "net.tcp://localhost:8001/MyService/"
           
bindingConfiguration = "TransactionalTCP"
          
binding  = "netTcpBinding"
            contract = "IMyOtherContract"
         />
      </service>
   </services>
  
<bindings>
      <netTcpBinding>
         <binding name = "TransactionalTCP"
            transactionFlow = "true"
         />
       </netTcpBinding>
    </bindings>
</system.serviceModel>

As shown in Example 1-8, you can reuse the named binding configuration in multiple endpoints simply by referring to it.

Programmatic Endpoint Configuration

Programmatic endpoint configuration is equivalent to administrative configuration. Instead of resorting to a config file, you rely on programmatic calls to add endpoints to the ServiceHost instance. Again, these calls are always outside the scope of the service code. ServiceHost provides overloaded versions of the AddServiceEndpoint() method:

  public class ServiceHost : ServiceHostBas
  {
     public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
                   Binding binding,
                   string address);
     //Additional members
  }

You can provide AddServiceEndpoint() methods with either relative or absolute addresses, just as with a config file. Example 1-9 demonstrates programmatic config uration of the same endpoints as in Example 1-7.

Example 1-9. Service-side programmatic endpoint configuration

ServiceHost host = new ServiceHost(typeof(MyService));

Binding wsBinding  = new WSHttpBinding(); Binding tcpBinding = new NetTcpBinding();

host.AddServiceEndpoint(typeof(IMyContract),wsBinding,  
          "http://localhost:8000/MyService"); host.AddServiceEndpoint(typeof(IMyContract),tcpBinding, 
      "net.tcp://localhost:8001/MyService"); host.AddServiceEndpoint(typeof(IMyOtherContract),tcpBinding, 
      "net.tcp://localhost:8002/MyService");

host.Open();

When you add an endpoint programmatically, the address is given as a string, the contract as a Type , and the binding as one of the subclasses of the abstract class Binding , such as:

  public class NetTcpBinding : Binding,.. .
  {…}

To rely on the host base address, provide an empty string if you want to use the base address, or just the URI to use the base address plus the URI:

  Uri tcpBaseAddress = new Uri("net.tcp://localhost:8000/");

  ServiceHost host = new ServiceHost(typeof(MyService),tcpBaseAddress);

  Binding tcpBinding = new NetTcpBinding();

  //Use base address as address
  host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"");
  //Add relative address
  host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"MyService");
  //Ignore base address
  host.AddServiceEndpoint(typeof(IMyContract),tcpBinding, 

  "net.tcp://localhost:8001/MyService");
  host.Open();

As with administrative configuration using a config file, the host must provide a matching base address; otherwise, an exception occurs. In fact, there is no difference between programmatic and administrative configuration. When you use a config file, all WCF does is parse the file and execute the appropriate programmatic calls in its place.

You can programmatically set the properties of the binding used. For example, here is the code required to enable transaction propagation similar to Example 1-8:

  ServiceHost host = new ServiceHost(typeof(MyService));

  NetTcpBinding tcpBinding = new NetTcpBinding();

  tcpBinding.TransactionFlow = true;

  host.AddServiceEndpoint(typeof(IMyContract),tcpBinding, 
      "net.tcp://localhost:8000/MyService");
  host.Open();

Note that when you’re dealing with specific binding properties, you typically inter act with a concrete binding subclass such as NetTcpBinding , and not its abstract base class Binding as in Example 1-9.

{mospagebreak title=Metadata Exchange}

A service has two options for publishing its metadata. You can provide the metadata over the HTTP-GET protocol, or you can use a dedicated endpoint, discussed later. WCF can provide the metadata over HTTP-GET automatically for your service; all you need is to enable it by adding an explicit service behavior. Behaviors are described in subsequent chapters. For now, all you need to know is that a behavior is a local aspect of the service, such as whether or not it wants to exchange its metadata over HTTP-GET. You can add this behavior administratively or programmatically. Example 1-10 shows a host application config file, where both hosted services reference a custom behavior section that enables the metadata exchange over HTTP-GET. The address the clients need to use for the HTTP-GET is the registered HTTP base address of the service. You can also specify in the behavior an external URL for this purpose.

Example 1-10. Enabling metadata exchange behavior using a config file

<system.serviceModel>
  
<services>
     
<service name = "MyService" behaviorConfiguration = "MEXGET">
        
<host>
           
<baseAddresses>
              
<add baseAddress = "http://localhost:8000/"/>
           
</baseAddresses>
        
</host>
        
.. .
      </service>
      <service name = "MyOtherService" behaviorConfiguration = "MEXGET">
         <host>
            <baseAddresses>
               <add baseAddress = "http://localhost:8001/"/>
           
</baseAddresses>
         </host>
         …
     
</service>
      </services>
      <behaviors>
         <serviceBehaviors>
          <behavior name = "MEXGET"> 
             <serviceMetadata httpGetEnabled = "true"/>
         </behavior>
      </serviceBehaviors>
   </behaviors>
</system.serviceModel>

Once you have enabled the metadata exchange over HTTP-GET, you can navigate to the HTTP base address (if present) using a browser. If all is well, you will get a con firmation page, such as the one shown in Figure 1-6, letting you know that you have successfully hosted a service. The confirmation page is unrelated to IIS hosting, and you can use a browser to navigate to the service address even when self-hosting.

Enabling Metadata Exchange Programmatically

To programmatically enable the metadata exchange over HTTP-GET, you first need to add the behavior to the collection of behaviors the host maintains for the service type. The ServiceHostBase class offers the Description property of the type ServiceDescription :

  public abstract class ServiceHostBase : …
 
{
     public ServiceDescription Description
     {get;}
     //More members
 
}

The service description, as its name implies, is the description of the service with all its aspects and behaviors. ServiceDescription contains a property called Behaviors of the type KeyedByTypeCollection<I> with IServiceBehavior as the generic parameter:

  public class KeyedByTypeCollection<I> : KeyedCollection<Type,I>
 
{
     public T Find<T>();
     public T Remove<T>();
     //More members
 
}
  public class ServiceDescription
  {
    
public KeyedByTypeCollection<IServiceBehavior> Behaviors
     {get;}
  }


Figure 1-6.  A service confirmation page

IServiceBehavior is the interface that all behavior classes and attributes implement. KeyedByTypeCollection<I> offers the generic method Find<T>() , which returns the requested behavior if it is in the collection, and null otherwise. A given behavior type can only be found in the collection at most once. Example 1-11 shows how to enable the behavior programmatically.

Example 1-11. Enabling the metadata exchange behavior programmatically

ServiceHost host = new ServiceHost(typeof(MyService));

ServiceMetadataBehavior metadataBehavior; metadataBehavior = host.Description.Behaviors.Find <ServiceMetadataBehavior>();
if(metadataBehavior == null)

  
metadataBehavior = new ServiceMetadataBehavior();
 
metadataBehavior.HttpGetEnabled = true;
 
host.Description.Behaviors.Add(metadataBehavior);
}

host.Open();

First the hosting code verifies that no MEX endpoint behavior was provided in the config file by calling the Find<T>() method of KeyedByTypeCollection<I> using ServiceMetadataBehavior as the type parameter. ServiceMetadataBehavior is defined in the System.ServiceModel.Description :

  public class ServiceMetadataBehavior : IServiceBehavior
  { 
  
  public bool HttpGetEnabled
    
{get;set;}
    
//More members
  }

If the returned behavior is null , the hosting code creates a new ServiceMetadataBehavior , sets HttpGetEnabled to true , and adds it to the behaviors in the service description.

Please check back next week for the continuation of this article.

 

One thought on “WCF and Bindings

  1. This article is an excerpt from the book “Programming WCF Services,” published by O’Reilly. We hope you found it to be enjoyable and educational. Please let us know what you thought of it, and if you would like to see more content of this nature.

[gp-comments width="770" linklove="off" ]