Sunday 20 January 2013

Serialization and Deserialization woes in WCF

WCF is a great technology at getting a service up and running in quick time on any kind of transport. Another great thing about WCF is that it is ultra extensible. You can replace almost every default implementation - Authorization, Serialization, Validation, etc.
 
I ran into an interesting situation recently. I had WSDL and XSD schema files which had already been shared with consumers of the WCF service. Due to the way WSDL and XSD were written (I guess hand coded), svcutil.exe and wsdl.exe were not able to generate appropriate proxy. Then XSD.exe came to rescue. I created the C# classes for all the types defined in XSD schema files and manually created the [ServiceContract] and [OperationContract]. All good till now.
 
As I started testing the application using SoapUI, I ran into following problems (in the below specified order):
 
1. "SOAP Action not supported". After a bit of troubleshooting it became clear that WCF service was expecting a default SOAP action for each method as hand coded [ServiceContract] and [OperationContract] was using default options.
 
To resolve the problem, I found the expected SOAP action from the WSDL file and updated the OperationContract to include the action. e.g. [OperationContract(Action="ExpectedAction")]
 
2. Input parameters to the WCF service were null. It turned out that the deserialization process was not working properly.
 
To resolve the issue, I had to manually update the DataCotnract classes as well. Apparently the SOAP message body was not expected to the wrapped and the default implementation is to wrap request and response. I changed the data contracts to use [MessageContract[IsWrapped=false)] to fix the problem.
 
And while I was doing the above, re-learned a few things about XmlSerialization.
1. If a class has an optional value type property, then it is expected that you add a Boolean property named [PropertyName]Specified and decorate it with [XmlIgnore]. This companion property is used to find out if value needs to be considered during serialization and deserialization. e.g.
[XmlElement]
public int Age { get { return _age;}  set { _age = value, AgeSpecified= true;} }
[XmlIgnore]
public bool AgeSpecified { get; set;}
2. How do you send empty string for fields that have data type like DateTime or an Enum. Simple - Add additional properties of String data type to the class and decorate those with XmlElement (with desired name) and mark the actual fields with XmlIgnore. e.g.
[XmlElement("BirthDate"]
public string BirthDateTimeString { get; set;}
[XmlIgnore]
public DateTime BirthDate { get; set;}

No comments:

Post a Comment