Version tolerance with WCF

By | June 4, 2015

As I stated earlier in a post; “WCF is version tolerant it does not have specific version support. It will adjust the data contracts according to what the client and service is expecting.” The IExtensibleDataObject is the key to have version tolerance.

The Microsoft site states the following about this interface.

The IExtensibleDataObject interface provides a single property that sets or returns a structure used to store data that is external to a data contract. The extra data is stored in an instance of the ExtensionDataObject class and accessed through the ExtensionData property. In a roundtrip operation where data is received, processed, and sent back, the extra data is sent back to the original sender intact. This is useful to store data received from future versions of the contract. If you do not implement the interface, any extra data is ignored and discarded during a roundtrip operation.

I have prepared a sample demonstrating how version tolerance is achieved by applying this interface. The sample can be downloaded here. The sample contains 3 clients (3 versions) that I build and a service that i continued to evolve. I never go back to refresh the client to bring it back in-line which means that the client becomes out of sync with the service but yet still continues to work.

The service

The service has 2 operations a Get and Set. The set operation sets a private static variable inside the service that the next get operation retrieves. This will allow me to show how a data contract is version tolerant between versions.

[ServiceContract]
public interface IService1
{
[OperationContract]
CompositeType GetDataUsingDataContract();

[OperationContract]
void SetDataUsingDataContract(CompositeType composite);
}

The Set operation will write the data contract data out to the screen.

IExtensibleDataObject_ServerConsole_01

The client.

The client console that i wrote provides 4 functions.

  1. To close the client press (Q)
  2. It allows the clients to (S) submit data to the service
  3. It allows the clients to (R) retrieve data from the service
  4. It allows the client to (A) retrieve data from the service and alter it. It will only be altering the version member of the data contract. This is the function that will show the version tolerance between the clients.

When data is submitted it will print out what is being submitted.

Version1Submit_01

The server will respond by printing out the data that it has received. Note that because the service is evolving and the client not the data on the 2 screen might not always match up.

IExtensibleDataObject_ServerConsole_02

Data Contracts

I started off with a basic data contract (Version 1) that implements the IExtensibleDataObject interface. Note that the client version member in the data contract below is used to show which client submitted the data, it is not needed to determine the version.  The client service reference was generated by adding a service reference in visual studio.

I am going to list the different versions of the data contracts below.

Version 1.

[DataContract]
public class CompositeType : IExtensibleDataObject
{
[DataMember]
public int ClientVersion { get; set; } //

[DataMember(IsRequired=true)]
public String RequiredField{get; set;}

[DataMember]
public String Version1FieldMemberToDelete{get; set;}

public ExtensionDataObject ExtensionData { get; set; }
}

The version 1 of the contract starts of with (1) the Client Version (2) a required member, (3) a member that will be deleted in version 2 and (4) the IExtensibleDataObject  interface implementation.

Version 2

[DataContract]
public class CompositeType : IExtensibleDataObject
{
[DataMember]
public int ClientVersion { get; set; } //

[DataMember(IsRequired=true)]
public String RequiredField{get; set;}

[DataMember]
public string Version2Field { get; set; }

public ExtensionDataObject ExtensionData { get; set; }
}
}

In Version 2 of the contract the member “Version1FieldMemberToDelete” has been removed and a new member “Version2Field” has been added.

Version 3

[DataContract]
public class CompositeType : IExtensibleDataObject
{
[DataMember]
public int ClientVersion { get; set; } //

[DataMember(IsRequired=true)]
public String RequiredField{get; set;}

[DataMember]
public string Version2Field { get; set; }

[DataMember]
public string Version3Field { get; set; }

public ExtensionDataObject ExtensionData { get; set; }
}

A new member called Version3Field has been added.

The Test

So right now, we have a service that is at version 3 and 3 clients, one at each version. I am going to demonstrate how WCF is version tolerant in these situations.

  1. Version 1 client submits data to the server.
    Version1Submit_01
    .
    The server prints out the received data. Note that the field “Version1FieldMemberToDelete” is present in the client but the server which is at version 3 does not know about it. The server also prints out 2 additional fields Version2Field and Version3Field.
    IExtensibleDataObject_ServerConsole_03
  2. Next the version 2 client requests the data. Here we see that the client that submitted the data was a version 1 client. The version 2 client does not see the “Version1FieldMemberToDelete”  member. It is only aware of the members that are present in version 2.
    Version2Read_01
  3. Now the version 3 client does (A) requesting the data from the server and adjusting it. Note that additional fields are now populate by the Adjustment.
    Version3Adjust_01
    The server reports that it has received a new submission from a version 3 client.
    IExtensibleDataObject_ServerConsole_04
  4. Now the version 1 client requests the data back from the service. Note that the version of the client has been updated and that the field “Version1FieldMemberToDelete” has not been losts although it has been read and resubmitted by a version 3 client. The IExtensibleDataObject interface preserved the data for us.
    Version1Read_02
  5. If we now read the data from a version 2 client. You will see that it can see the data that version 3 client filled in.
    Version2Read_02

So as you can see IExtensibleDataObject interface provides seamless version tolerance.

How it works?

When the client or service de-serializes the incoming data it tries to match the incoming data to the data members to its own data contract. If there is more data than what matches the signature of its own data contract, the additional data is stored in the ExtensionData property. The de-serialization process also checks for data matching its own data contract inside the ExtensionData property. By using this method no data is ever lost and unknown data is always stored.

This always for version tolerance.

There are situations where it is not possible to preserve version tolerance. As soon as a new “Required field” is introduced all backwards compatibility is lost. As a test try and use this 4th version of the data contract. Note that Version3Field is now marked as required. The end result will be that none of the other clients will work any more.

[DataContract]
public class CompositeType : IExtensibleDataObject
{
[DataMember]
public int ClientVersion { get; set; } //

[DataMember(IsRequired=true)]
public String RequiredField{get; set;}

[DataMember]
public string Version2Field { get; set; }

[DataMember(IsRequired = true)]
public string Version3Field { get; set; }

public ExtensionDataObject ExtensionData { get; set; }
}

Leave a Reply

Your email address will not be published.