It’s obvious that every software is something breakable if we planning to improve it with a new version and the same story is with our WCF services which can evolve over time. In this article, I’ll gave you few advices on dealing with different versioning scenarios on WCF data contracts. There are two sets of data contract versioning guidelines: one set for scenarios where strict schema validity is important and another set for scenarios when it is not. For the second option, where strict schema validity is not important, there are few things explained in this article to be known if you like to do it in non-breakable way.
There are several versioning scenarios for data contracts. Possible changes are summarized in the following list as possible changes to a data contract and the affect it has on existing clients:
Making changes to data contracts once they have been published is a delicate matter. Even if no exception is thrown for the change, the data may lack integrity and to avoid this, a few general guidelines should be followed if you are not creating a new data contract version:
The decision flow after DataContract is changed is shown on the following diagram:
As base code, I’ll use very simple DataContract during the scenarios to show different decision as solutions:
[DataContract] public class Customer { [DataMember] public string Name; [DataMember] public string VatNumber; }
Optional members is something which is feasible using the parameter Required on DataMember attribute such as on the following example where the new field Address is added to our DataContract:
[DataMember(IsRequired = false, EmitDefaultValue = true)] public string Address;
This will enable for old proxies to work the same as they worked before and the value of the Address should be null. It’s a good practice to provide default value for this field if there are particular usage and/or calculations on this field using the parameter EmitDefaultValue set to true. In this case, because the Address is of type string, the default value is empty string.
Round-tripping occurs when data passes from a new version to an old version and back to the new version of a data contract. Round-tripping must guarantee that no data is lost. Enabling round-tripping makes the type forward-compatible with any future changes supported by the data contract versioning model.
So, let’s enable round-tripping for a simple particular type. First we need to implement the IExtensibleDataObject interface, which contains one property, ExtensionData and returning the ExtensionDataObject type. This property stores any data from future versions of the data contract that is unknown to the current version.
The following data contract is not forward-compatible with future changes.
To make sure that this type will preserve compatibility with future changes we need to implement the IExtensibleDataObject interface.
[DataContract] public class Customer: IExtensibleDataObject { [DataMember] public string Name; [DataMember] public string VatNumber; public ExtensionDataObject ExtensionData { get; set; } }
What happens at the client side is that SvcUtil generates data contracts that implement IExtensibleDataObject as well. This ensures that extra data sent to clients by the service will not be lost in a round trip. On either side the result is that the DataContractSerializer populates the ExtensionDataObject dictionary on deserialization, and uses the same dictionary to serialize additional elements in outgoing messages.In this case beware from large superfluous data sent from the client side!
For best practices on Data Contract versioning, also take a look on this MSDN article.