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