DCSIMG
Your service generates odd WSDL? Search for Serializable attribute - Ran Wahle's blog

Ran Wahle's blog

Your service generates odd WSDL? Search for Serializable attribute

A problem I’ve encountered at a customer I work for was very strange behavior regarding
the WSDL being generated by it’s service. The WSDL didn’t contain any class property, moreover it did contain private members of a class being exposed by the service.

Let’s have a look on a repro:

1. Here’s the class being exposed:

namespace SerializationDemo.Entities
{
    [Serializable]
    public class SerializableEntity
    {
        public int SomeProperty { get; set; }
    }
}

2. Here’s the class schema representation as being exposed in WSDL

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities">
<xs:complexType name="SerializableEntity">
<xs:sequence>
<xs:element name="_x003C_SomeProperty_x003E_k__BackingField" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:element name="SerializableEntity" nillable="true" type="tns:SerializableEntity"/>
</xs:schema>

3. Here’s the class as generated on client side

[System.Diagnostics.DebuggerStepThroughAttribute()]
   [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
   [System.Runtime.Serialization.DataContractAttribute(Name="SerializableEntity", Namespace="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities")]
   [System.SerializableAttribute()]
   public partial class SerializableEntity : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
       
       [System.NonSerializedAttribute()]
       private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
       
       private int SomePropertyk__BackingFieldField;
       
       [global::System.ComponentModel.BrowsableAttribute(false)]
       public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
           get {
               return this.extensionDataField;
           }
           set {
               this.extensionDataField = value;
           }
       }
       
       [System.Runtime.Serialization.DataMemberAttribute(Name="<SomeProperty>k__BackingField", IsRequired=true)]
       public int SomePropertyk__BackingField {
           get {
               return this.SomePropertyk__BackingFieldField;
           }
           set {
               if ((this.SomePropertyk__BackingFieldField.Equals(value) != true)) {
                   this.SomePropertyk__BackingFieldField = value;
                   this.RaisePropertyChanged("SomePropertyk__BackingField");
               }
           }
       }
       
       public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
       
       protected void RaisePropertyChanged(string propertyName) {
           System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
           if ((propertyChanged != null)) {
               propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
           }
       }
   }

 

As seen from the WSDL and from the auto-generated client code, instead of getting the property SomeProperty of SerializableEntity you get the class's private  field, which was the compiler-generated backing field (_x003C_SomeProperty_x003E_k__BackingField) of the automatic property.

Why did it happened?

SerializableAttribute indicates that all fields of the class marked Serializable can be serialized, including of course the private ones. However, it doesn’t say anything about the properties

When getting the entity schema, the WCF invokes the the default formatter, having the class marked Srializable  only (without DataContract) causes all fields to be serialized but no property is. Therefore we’ll see no property but private member.
That’s because the Serializable attribute aimed at binary serialization which serializes the data and not the methods. And data stored in fields and not in properties that are nothing more than methods accessing fields.

Solutions:

1. One solution is to have all service methods exposing the Serializable marked type use the XmlSerializerFormat simply by marking the method’s declaration at the service contract. This will cause the WSDL to be generated using XML serializer and will ignore the Serializable attribute and will serialize all public members including read-write properties.
This solution is good when the entities being exposed are in external DLLs or we can’t change their code for some reason.

2. At the exposed entities, add the DataContract attribute with DataMember attribute on each member we’d like to expose. After doing so, the  DataContractFormat  (which is the default format being used) will ignore the Serializable  attribute and use only the DataMember attribute to serialize the schema.

Summary:

Serializable attribute aimed at binary serialization, which uses all data in the class which stored in fields and not in properties. Therefore, colliding with DataContract serialization it produces an odd behavior of serializing all fields, including private ones and no property.

Using XmlSerializerFormat on the method declarations or DataContract on the very entity being exposed can solve the problem.

 

kick it on DotNetKicks.com

Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# November 10, 2011 5:19 PM

Anorionil said:

Thank you, this totally helped me out!

# December 3, 2012 10:54 AM

Ran Wahle said:

Great to read that Anorionil

# December 3, 2012 2:53 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: