Versioning Windows Communication Foundation Services

 

To show how to properly deal with versioning, I’ve created a versioning decision tree. It shows you what you will need to do to handle every case, and it also shows you the consequences of each versioning choice you might make.

The white diamonds are versioning decisions, decisions about how you might version a service. The squares represent actions you will need to take as a result of your versioning choices. Squares that are green represent non-breaking changes. Squares that are red represent breaking changes. Naturally, and this is one of the first versioning practices you can derive from the decision tree, you want to avoid decisions that lead to breaking changes, that lead to red squares.

So let us follow the decision tree. You have a service, and now you need to modify it.

Can you meet your objectives in modifying the service by adding a new operation to the service? If so, then that is readily accomplished through service contract inheritance. Simply define a new service contract that derives from the service contract of the existing service, add the new operations to the new service contract, and then have your service type, the class that implements the existing service contract, implement the new service contract instead of the old one. Update the definition of the service endpoint so that it refers to the new, derived service contract. Now existing clients, which will only know about the old service contract, can have the operations that they already knew executed at the original endpoint, and new clients, which know about the enhanced service contract, can have the additional operations executed at the same endpoint.

[ServiceContract]
public interface IMyServiceContract
{
[OperationContract(IsOneWay=true)]
public void MyMethod(MyDataContract input);
}

[ServiceContract]
public interface IMyAugmentedServiceContract: IMyServiceContract
{
[OperationContract(IsOneWay=true)]
public void MyNewMethod(MyOtherDataContract input);
}  

public class MyOriginalServiceType: IAugmentedServiceContract

If your requirements for modifying the service cannot be met by adding new operations to the service, do you then have to change an existing operation? If so, then how exactly do you need to change it?

Do you need to change the structure of the data passed to the service by its clients? If so, then you need to version a data contract.

How do you need to modify the data contract? If it is simply a matter of incorporating more data into the data contract, then that is easy to do, simply by adding new, optional members to the data contract.

It is a good practice to implement the IExtensibleDataObject interface on all data contracts, so that if you have to process an instance of an enhanced version of the data contract, that includes members of which you are not aware, then the data for those additional members can pass through your code without being lost. Implementing IExtensibleDataObject provides the data contract serializer with a place to store the additional data on the way into your code, from which the data can be retrieved again on the way out.

[DataContract]
public class MyDataContract: IExtensibleDataObject
{
[DataMember(IsRequired=true)]
public XType MyOriginalMember;

 [DataMember(IsRequired=false)]
public XType MyNewMember;

 private ExtensionDataObject extensionData;
public ExtensionDataObject ExtensionData
{
get
{
return this.extensionData;
}

set
{
this.extensionData = value;
}
}
}

When you are forced to make other changes to a data contract besides simply adding members, then you must take the steps shown in the decision tree.

  1. You must define a new version of the data contract in a new namespace, and the naming of your namespaces should reflect your version numbers.
  2. You must define a new version of your service contract with an operation that uses the new version of the data contract. Once again, the namespace of the service contract should identify the version.
  3. You must create a new endpoint where the new service contract is exposed.
  4. Finally, you must decide whether to retire the existing endpoint.

 

If the requirements for the new version of your service require you to make any other kinds of changes to an operation defined in a service contract, or delete any operations from a service contract, or change the bindings of a service, then these are the steps you must take:

  1. You must define a new version of your service contract with an operation that uses the new version of the data contract, with the namespace identifying the version.
  2. you must create a new endpoint where the new service contract is exposed.
  3. You must decide whether to retire the existing endpoint.

 

Only the decision to retire an existing endpoint constitutes a breaking change. To make it easier to retire endpoints, you should include a default operation for every service contract that you ever define, and you should specify that operation can throw a fault indicating that the endpoint has been retired. The information provided with the fault can direct the client to the metadata for the replacement endpoints.

[DataContract]
public class RetiredEndpointFault
{
[DataMember]
public string NewEndpointMetadata
}

[ServiceContract]
public interface IMyServiceContract
{
[OperationContract(IsOneWay=true)]
public void MyMethod(MyDataContract input);

 [FaultContract(typeof(RetiredEndpointFault))]
[OperationContract(Action=“*”,ReplyAction=“*”)]
public Message MyDefaultMethod(Message input);

 Leveraging the default operation and fault contract facilities of the Windows Communication Foundation in this way yields the same benefits with far less effort than the facade service approach that is recommended here and here.