Versioning Windows Communication Foundation


Two types of Changes:

  • Backwards-Compatible Changes (or Non-Breaking Changes) -> Changes to services that would not require changes to existing clients.
     
  • Non-Backwards-Compatible Changes (or Breaking Changes) -> Changes to services that would require changes to existing clients.

Versioning Decision Tree

Versioning1.gif
Image courtesy: http://blogs.msdn.com/b/craigmcmurtry/archive/2006/07/23/676104.aspx

It depicts all the logically possible ways in which we might have to modify a service, how to implement each change, and what the consequences will be.

The diamonds represent alternative ways in which a service might have to change. The rectangles and the circle represent procedures for implementing changes. The procedures represented by the rectangles yield backwards-compatible changes, whereas the procedure represented by the circle results in a non-backwards-compatible change.

Now let us trace the routes through the decision tree to understand the implications of each alternative.

1. Adding a New Operation
 
This change can be accomplished using a procedure called service contract inheritance, and the result will be a backwards-compatible change.

E.g.,
Currently, I am having this Service Contract

[ServiceContract]
public interface IEcho
{
[OperationContract]
string Echo(string input);
}
 
public class Service : IEcho
{
public string Echo(string input)
{
return input;
}

}

Now to add new operation we need to define a new service contract (IExtendedEcho), with the new operations to be added to the service, which derives from the original service contract (IEcho):

[ServiceContract]
public interface IExtendedEcho: IEcho
{
[OperationContract]
string[] ExtendedEcho(string[] inputs);
}

The next step is to have the service type implement the new contract in addition to the original one:

public class Service : IEcho, IExtendedEcho
{
public string Echo(string input)
{
return input;
}
public string[] ExtendedEcho(string[] input)
{
return input;
}
}


The final step is to modify the configuration of the service endpoint so that where it referred to the original contract

<endpoint
address="Echo"
binding="basicHttpBinding"
contract="IEcho"
/>

It now refers to the derived contract with the additional methods:

<endpoint
address="Echo"
binding="basicHttpBinding"
contract="IExtendedEcho"
/>

Now new clients that are aware of the additional operations of the derived contract can make use of those operations. Yet existing clients, which might know about only the original service contract, could still have the operations of that original contract executed at the same endpoint. Thus, service contract inheritance has the happy consequence of a backwards-compatible change.

2. Changing an Operation

The second decision posed by the versioning decision tree is whether or not an existing operation of a service has to be modified. If so, the next decision to make is whether or not the change that is required is a change to the data contracts of one or more parameters.

Changing the Data Contract of a Parameter

1. Adding\Removing nonrequired data members

[DataContract (Namespace=http://WCFVersioning/2011/09)]
public class StockPrice
{
      [DataMember(IsRequired=true)]
        public double CurrentPrice;
 
[DataMember(IsRequired=true)]
        public DateTime CurrentTime;
 
[DataMember(IsRequired=true)]
     
  public string Ticker;

[DataMember]
        public string Currency;
       }

[DataContract (Namespace=http://WCFVersioning/2011/09)]
public class StockPrice
{
      [DataMember(IsRequired=true)]
        public double CurrentPrice;
 
[DataMember(IsRequired=true)]
        public DateTime CurrentTime;
 
[DataMember(IsRequired=true)]
        public string Ticker;
 
[DataMember]
        public int DailyVolume;

       }

The addition and removal of optional data members is a backwards-compatible change.

For existing clients to properly pass around data after new members are added, the original data contract must support extensibility. That is, the original contract must support serialization of unknown future data.

A Data Contract that Implements IExtensibleDataObject

[DataContract (Namespace=http://WCFVersioning/2011/09)]
public class StockPrice : IExtensibleDataObject
{
[DataMember(IsRequired=true)]
        public double CurrentPrice;
 
[DataMember(IsRequired=true)]
        public DateTime CurrentTime;
 
[DataMember(IsRequired=true)]
        public string Ticker;
 
[DataMember]
        public string Currency;
      
private ExtensionDataObject unknownData = null;
public ExtensionDataObject ExtensionData
{
get { return this.extensionData; }
 
set { this.extensionData = value; }
}
  }

Other Changes to Data Contracts

  • Change the name or namespace of a data contract

  • Rename an existing data member that was previously required.

  • Add a new data member with a name that has been used previously.

  • Change the data type of an existing data member.

  • Add new members with IsRequired=true on DataMemberAttribute.

  • Remove existing members with IsRequired=true on DataMemberAttribute.

2. Adding required data members

[DataContract (Namespace=http://WCFVersioning/2011/09)]
public sealed class ExServiceDef : IExtensibleDataObject
{
[DataMember(Order = 1)]
public string UploadUrl { get; set; }

[DataMember(Order = 2)]
public string Version { get; set; }

[DataMember(IsRequired=true, Order = 3)]
public string NewRequiredField { get; set; }

#region IExtensibleDataObject Members

public ExtensionDataObject ExtensionData { get; set; }

#endregion
}

Client Request

<ServicesForUsers xmlns="http:// WCFVersioning/2011/09">
<
securitytoken>123</securitytoken>
<
username>ZodiacTest</username>
</
ServicesForUsers
>

Service Response

<ServicesForUsersResponse xmlns=" http:// WCFVersioning/2011/09">
<
ServicesForUsersResult xmlns:d4p1="http://WCFVersioning/2011/09/DataContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<
d4p1:UploadUrl>\\nimittal\MRDF\JobFiles\</d4p1:UploadUrl>
<
d4p1:Version>1.0.0.0</d4p1:Version>
<d4p1:NewRequiredField i:nil="true"></d4p1:NewRequiredField>
</ServicesForUsersResult>
</
ServicesForUsersResponse>
</
s:Body
>

The addition of required data members is a backwards-compatible change.

3. Removing required data members

The removal of required data members is a non-backwards-compatible change.

If we have removed the required data members and not updated client WSDL we will get error message something like this:

Error Message:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://WCFVersioning/2011/09:GetCustomerResult. The InnerException message was 'Error in line 1 position 4874. 'EndElement' 'GetCustomerResult' from namespace 'http://WCFVersioning/2011/09 is not expected. Expecting element 'NewRequiredField'.'. Please see InnerException for more details

3. Other Changes to Operations

Changes to existing operations:

  • Changing the name of the operation

  • Adding/Deleting a parameter

  • Deleting an operation

These all changes can be dealt with by defining a new service contract incorporating the modified operation. Revised versions of service contracts must be disambiguated
from earlier versions by defining them in new version-specific namespaces. Also, after a new version of a service contract has been defined, it should be exposed at a new service endpoint. Again, exposing a new service endpoint is a backwards-compatible change.

4. Changing a Binding

If the change to be made to a service requires changing the binding of one of its endpoints, the versioning decision tree indicates that the modified binding should be
exposed at a new service endpoint. Exposing a new service endpoint is a backwards-compatible change.

5. Deciding to Retire an Endpoint

If we want to retire an existing endpoint that means we are introducing new endpoint for all existing client. This is a non-backwards-compatible change.

There are two ways of easing the consequences of this change to a service:

  1. Add a System.ServiceModel.FaultContract attribute to all the operations of a contract to indicate that the operation might return a fault indicating that the endpoint has been retired:

    [DataContract]
    public class RetiredEndpointFault
    {
    [DataMember]
    public string NewEndpointMetadataLocation;
    }

    [ServiceContract]
    public interface IEcho
    {
    [FaultContract(typeof(RetiredEndpointFault))]
    [OperationContract]
    string Echo(string input);
    }
     

  2. Inform all the clients using the existing endpoint and also the operators of those clients.

Service Contract Changes

Change

Type of Change

Effect

Adding a new operation

Non-Breaking Change

Existing client applications are

unaffected, but the new operation is not visible to WCF client applications connecting to the service by using a proxy generated from the WSDL description of the original service contract.

 

Existing client applications that dynamically query services and construct messages can use the new operation.

Removing an operation

Breaking Change

Existing client applications that

invoke the operation will no longer function correctly, although client applications that do not use the operation remain unaffected.

Changing the name of an operation

Breaking Change

Existing client applications that

invoke the operation will no longer work, although client applications that do not use the operation remain unaffected.

 

Note that the name of an operation defaults to the name of the method in the service contract. You can change the name of a method but retain the original name of the operation by using the Name property in the OperationContract attribute of the method, like this:

 

[OperationContract (Name="ListProducts")]

List<string> ListAllProducts();

 

This is good practice, as it removes any dependency between the service contract and the name of the physical method that implements the operation.

Changing the protection level of an operation

Breaking Change

Existing client applications will not be able to invoke the operation.

Adding a parameter to an operation

Breaking Change

Existing client applications will no

longer be able to invoke the operation, as the SOAP messages they send will be incompatible with the SOAP messages

expected by the service.

Reordering parameters in an operation

Breaking Change

The results are not easily predictable (some existing client applications might continue to work).

Removing a parameter from an operation

Breaking Change

The results are not easily predictable (some existing client applications might continue to work).

Changing the types of parameter or the return type of an operation

Breaking Change

Existing client applications might

continue to function, but there is a significant risk that data in SOAP messages will be lost or misinterpreted. This includes

applying or removing the ref and out modifiers to parameters, even if the underlying type does not change.

Adding a Fault Contract to an Operation

Breaking Change

Existing client applications can be sent fault messages that they will not be able to interpret correctly.

Removing a Fault Contract from an operation

Non-Breaking Change

Existing client applications will continue to function correctly, although any handlers for trapping the faults specified by this fault contract will be rendered obsolete.

Changing the Name or Namespace property of the ServiceContract attribute for a service contract

Breaking Change

Existing client applications that use the previous name or namespace will no longer be able to send messages to the service.

Best Practices:

  • For implementing Breaking Changes, create a new version of service contract and leave the existing version intact.
     


Similar Articles