Change the Order of Data Members in Data Contracts in WCF

Programming with Windows Communication Foundation requires you to have some specific knowledge for this technology (as any other technology). One of the several things that I believed about WCF in the past three years of working with this technology is the fact that there is always a point that you don't know about it.

In the current post I'm going to talk about one of these simple and maybe obvious points that I didn't know and just found it recently.

Usually we work with an upper level of working with WCF messages in our applications unless we need to work in a lower level and manipulate the messages. So we usually define some classes and write our code and leave the rest to WCF (usually is much different from always especially in this case). One of the several things that we leave for WCF is managing the structure of XML, Json or any other representational format of our data contracts.

But there are some cases that you need to take care about this representation. One example is where you write a WCF service that has to respond to requests from unknown clients. Such clients may represent your data contract instances in different ways but WCF is sensitive to the representation.

First suppose that we have a data contract like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

 

namespace WCFDataContractElements

{

    [DataContract(Name = "Person", Namespace = "")]

    public class Person

    {

        [DataMember(Name = "FirstName", IsRequired = true)]

        public string FirstName { get; set; }

 

        [DataMember(Name = "LastName", IsRequired = true)]

        public string LastName { get; set; }

 

        [DataMember(Name = "BirthDate", IsRequired = true)]

        public DateTime BirthDate { get; set; }

 

        [DataMember(Name = "Hometown", IsRequired = false)]

        public string Hometown { get; set; }

    }

}

By default, WCF expects messages to have this data contract serialized into any format by putting its properties in a descending alphabetical order. So by default you have to serialize this data contract instances in an order like FirstName, LastName, BirthDate and Hometown. Any other order will cause an exception like what you see here (I first put Hometown element in the serialized message):

System.Runtime.Serialization.SerializationException: Error in line 1 position 20. 'Element' 'Hometown' from namespace '' is not expected. Expecting element 'FirstName'. at System.Runtime.Serialization.XmlObjectSerializerReadContext.ThrowRequiredMemberMissingException(XmlReaderDelegator xmlReader, Int32 memberIndex, Int32 requiredIndex, XmlDictionaryString[] memberNames) at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetMemberIndexWithRequiredMembers(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, Int32 memberIndex, Int32 requiredIndex, ExtensionDataObject extensionData) at ReadPostFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] ) at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract) at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns) at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName) at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName) at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName) at System.ServiceModel.Dispatcher.SingleBodyParameterMessageFormatter.ReadObject(Message message) at System.ServiceModel.Dispatcher.SingleBodyParameterMessageFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

There are two solutions to get this resolved. One solution is to modify your serialization logic to serialize elements in the correct order. But the other solution is to define a custom order for your elements on service side. DataMember attribute has an integer Order parameter that lets you set the order of a data member in the messages. Starting from zero, you can set the order by giving a larger number to an element that should be placed in the lower position. For example, in the below code I change the order of elements so Hometown goes first then BirthDate, then FirstName and LastName at the end.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

 

namespace WCFDataContractElements

{

    [DataContract(Name = "Person", Namespace = "")]

    public class Person

    {

        [DataMember(Name = "FirstName", IsRequired = true, Order = 2)]

        public string FirstName { get; set; }

 

        [DataMember(Name = "LastName", IsRequired = true, Order = 3)]

        public string LastName { get; set; }

 

        [DataMember(Name = "BirthDate", IsRequired = true, Order = 1)]

        public DateTime BirthDate { get; set; }

 

        [DataMember(Name = "Hometown", IsRequired = false, Order = 0)]

        public string Hometown { get; set; }

    }

}

This is very simple but believe it or not, I didn't know about the existence of such a parameter unless I faced the error and had no choice except customizing the order of the elements.

However, there are two points about this parameter as is described on MSDN: in a hierarchy level, the order of elements in the base types are preferred. Also if you don't give an order to some elements then they will appear first with the default alphabetical order.

[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.

4 Comments : 04.29.08

Feedbacks

Pingback from Dew Drop - April 30, 2008 | Alvin Ashcraft's Morning Dew

 avatar
#2
Teo
07.31.2008 @ 4:35 AM

I love you!!! :)

This info solved my problem, I was facing it for about 4 hours.

 avatar
#3
Parker Smart
11.16.2008 @ 11:22 AM

What about defining the Order in the parent class? I'd like the properties in the parent classes to resolve FIRST and then come down the chain (just like constructing), but that's not happening. I've defined the Order of one element in the base class as 1, but haven't defined anything else. Maybe I have to define ALL the orders for ALL of the contained classes? I'll keep trying...

 avatar
#4
yogesh
01.06.2009 @ 9:10 AM

This is really interesting, I was trying to solve this problem from last 3/4 hours. it was something I never think of in WCF

Leave a Comment