WCF Service Hosting - Supporting Multiple Versions

This wiki refers to a sample project posted on MSDN: WCF Sample Service with Callbacks (wsdualhttpbinding)

In this technet wiki, I wanted to highlight a relatively simple strategy to handle the support of multiple service versions that were used in the sample project.

Background

In SOA, there is a requirement to maintain a strategy for handling change. In this context, change is primarily rolling out new versions of services without breaking existing clients. In some enterprises this might be a contractual requirement. In other situations it could be related to a strategy of a staged rollout where two versions of the same service exist simultaneously for a period of time.

Version 1

The initial version of the service was created that supported three operations for retrieving customer details:

[ServiceContract(Namespace = "uri:wcfsampledataservice:v1", Name = "IDataService")]
public interface  IDataService
{
    [OperationContract]
    Customer GetCustomer(int customerId);
 
    [OperationContract]
    CustomerAddress GetCustomerAddress(int customerId);
     
    [OperationContract]
    CustomerInvoices GetCustomerInvoices(int customerId);        
}

In the above, it is important to note that the initial namespace of the service is uri:wcfsampledataservice:v1. This contract namespace allows us a relatively simple way of isolating change from the clients.

Version 2

At a later stage, the service required an upgrade from basic binding to a binding that supported callbacks. This required a new contract (note, the namespace versioning):

[ServiceContract(CallbackContract = typeof(IDataCallback), Namespace = "uri:wcfsampledataservice:v2", Name = "IDataService")]
public interface  IDataServiceV2
{
    [OperationContract]
    Customer GetCustomer(int customerId);
}

  The callback contract is specified as a separate interface and is shown below:

public interface  IDataCallback
{
    [OperationContract(IsOneWay = true)]
    void OnCustomerAddressComplete(CustomerAddress data);
 
    [OperationContract(IsOneWay = true)]
    void OnCustomerInvoicesComplete(CustomerInvoices data);
}

 The new service version will spawn two threads on the GetCustomer() method and when each one completes a callback will be performed with the result.

Hosting

 

The desire is to have two versions supported at the same time. Instead of having two code bases for the different services, a single service implementation is chosen. In practice this is a single service that implements both contracts as shown below:

public class  DataService : ServiceHost,  IDataService, IDataServiceV2

The advantage here is one version of the service exists so only one code base needs to be maintained. The disadvantage is there is now a inter-dependency between the two versions and therefore a potential of unintentionally affecting one service when working on the other.

To carry this technet a bit farther, the hosting of the service is also simplified as the new service contract is exposed as a new endpoint allowing us to take advantage of having a single application for our solution:

<system.serviceModel>
  <services>
    <service behaviorConfiguration="DataServiceBehavior" name="WCFSampleDataService.DataService">
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost/:8755/DataService.svc" />
        </baseAddresses>
      </host>
      <endpoint address="basic" binding="basicHttpBinding" name="basic" contract="WCFSampleDataService.IDataService" />
      <endpoint address="dual" binding="customBinding" bindingConfiguration="duplexConfig" name="dual" contract="WCFSampleDataService.IDataServiceV2" />        
    </service>
  </services>    
</system.serviceModel>

This would then host the two versions of the service at http://localhost:8755/DataService.svc/basic and http://localhost:8755/DataService.svc/dual