Monday, June 30, 2008

ExtensibleDataObject and Data Member attributes


So far we have seen simple usage of DataMember’s in DataContract. We have plenty more support for them, let’s look into a few of them

    1. IsRequired – Do clients need to provide value to this field necessarily
    2. Order – The order in which the fields will be applied in SOAP exchange, so that fields can be processed accordingly


Here is a sample of overriding them

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}
[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

Let’s carry this discussion of, “IsRequired” attribute further. If we say IsRequired=false for a field, then we have 2 possibilities from client

    1. Client provides a value – Then service receives the value provided
    2. Client does not provide a value – Default value for the field is applied


In course of time services undergo modifications, some of the possibilities are

    1. Changes to Data members

      a. Change data type of fields – Clients will be affected if new type is not compatible with existing types, else they are compatible
      b. Addition of new data members – Old clients don’t complain, new clients will use new members
      c. Remove DataMembers – If the field removed is a required field then, clients will complain, otherwise they would not. This will lead to an interesting discussion that we pursue next
      d. Add new operations – Same as Addition of new data members
      e. Change signature of operations – If the old type are compatible, then no complaints, otherwise new version has to be drafted
      f. Remove operations – If clients have been using it, then expect them to complain


What will happen if a non-required (IsRequired=false) data member is removed from service, with client unaware of changes. Client keeps pushing that data and how will it be handled at service.

One way to handle this is Derive your DataContract class from IExtensibleDataObject interface. By doing this you have to implement ExtensionData field in your class. But the advantage is, if some old clients update properties/fields that have been chopped, they will get into ExtensionData field (instead of throwing an error), and we may opt to ignore or use it to keep track of number of client still hooked to old service

Here is a sample of it

[DataContract(Namespace = "http://techkrishnan.blogspot.com/2008/06")]
public class HouseContract : IExtensibleDataObject
{
private string owner;
private string tenant;
private DateTime startDate;
private DateTime endDate;
private string description;
private DateTime signedOn;

#region IExtensibleDataObject Members
ExtensionDataObject dataObject;
public ExtensionDataObject ExtensionData
{
get
{
return dataObject;
}
set
{
dataObject = value;
}
}
#endregion

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}

[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

[DataMember(IsRequired = false, Order = 6)]
public string Description
{
get { return description; }
set { description = value; }
}
}

Create a host application and client for this service

After successful execution, comment Description field in service code, without any changes to client or host code. Or may be you may wish to update reference for this dll in service host (that can be done)

Your new DataContract should look something like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace ExtensibleDataObjectService
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract(Namespace="http://techkrishnan.blogspot.com/2008/06")]
public interface IExtensibleObjectService
{
[OperationContract(IsInitiating=true)]
void AddContract(HouseContract contract);
// TODO: Add your service operations here
}

// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract(Namespace = "http://techkrishnan.blogspot.com/2008/06")]
public class HouseContract : IExtensibleDataObject
{
private string owner;
private string tenant;
private DateTime startDate;
private DateTime endDate;
private string description;
private DateTime signedOn;

#region IExtensibleDataObject Members
ExtensionDataObject dataObject;
public ExtensionDataObject ExtensionData
{
get
{
return dataObject;
}
set
{
dataObject = value;
}
}
#endregion

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}

[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

//[DataMember(IsRequired = false, Order = 6)]
//public string Description
//{
// get { return description; }
// set { description = value; }
//}
}
}

DataMember “Description” has been commented. Now restart host and client in order. Put a break point on the function AddContract in service and you can find description field in Extensiondata. Extension data will contain a dictionary that will have name-value pair. In this case, name will be “Description” and value, as assigned by client

Though it’s not that often we see a property get removed, this possibly shows a way to run on with out any issues with old and new clients


We’ll take a look at more option for OperationContract in the coming days


No comments: