Search code examples
c#.netwcfserializationdatacontract

Why does WCF not raise an error when DataContract is missing?


The problem I have is that I'm new to WCF, and just in the process of rigging up some WCF net.tcp client-server communication.

The connection worked fine, but when the call goes through to the server, the passed parameter object has all fields set to 0or null. This is especially fascinating as the type I'm passing cannot even be constructed to have all fields zero! (edit: actually, that would have been true if that type had been a class, but it is a struct, and struct in C# always has a parameterless constructor that sets all field to zero. The rest of the problem stands as described.)

After investigation, it seems that I must specify a DataContract for my custom parameters on the service. I'm fine with that, I'll add it and it will hopefully work, I have added DataContract to MyStruct (only) and it now works, but I fail to get why:

  • There is no compilation error
  • There is no runtime error (from WCF - my code fires an assertion for the invalid field)
  • WCF is apparently transferring a type and reconstructing objects without even using the c'tor of the objects. (and zero'ing out everything)

Why is that? Why is WCF transferring garbage without telling me? Is it a feature or a misfeature?


If it matters, here's a sketch of the types involved:

    ....
    [OperationContract]
    void Append(List<MyClassType> lines);

    ....
// Note: No DataContract here whatsoever
public class MyClassType
{
    private List<MyStruct> _values = new List<MyStruct>();

    public IList<MyStruct> Values
    {
        get { return _values; }
    }
....
// Note: No DataContract here whatsoever
public struct MyStruct
{
    readonly int m_tag;
    readonly float m_data;

    public MyStruct(float data)
    {
        m_tag = 1; // m_tag is *never* 0
        m_data = data;
....

On the server side, I receive a list of MyClassType with the Values list set to the correct number of MyStruct instances, but all the MyStruct object are zero'd out, even though regularly it is not even possible to construct such an object with m_tag set to zero!


Digging deeper (update): Answers so far have been helpful, but fail to address the why of my specific example, and also fail to address that it doesn't seem to make too much sense to transfer MyStruct - it doesn't even have a parameterless constructor! Specifically, I found Types Supported by the Data Contract Serializer, which states:

The DataContractSerializer ... supports many other types, which can be thought of as having an implicit data contract. The following is a complete list of types that can be serialized:

  • All publicly visible types that have a constructor that does not have parameters.
  • ...

MyStruct (below) clearly does not have a parameterless constructor, and still it is transferred and "reconstructed" using zero bytes. This seems to contradict the MSDN article. (Unless "not supported" means: "We'll silently transfer garbage without telling you.")



Solution

  • If your class has no serialization attributes recognised by WCF, then WCF will treat the class as if it was a DataContract with all public fields and properties as DataMembers. This behaviour was added in version 3.5, and since you are on version 4.0, you will see that behaviour. The version that your class was created with is not important, it's the WCF host that controls the behaviour.

    You see m_tag = 0 because the DataContractSerializer does not call any of your class constructors. It constructs a blank object using GetUninitializedObject, then it sets all field values from the deserialised data stream. If the m_tag element in the message contains 0, then the field will end up with that value. It will also be zero if the m_tag element is missing from the message, since WCF data contract members are optional by default.

    The best way to avoid problems like this is to regard all your data contracts as dumb data transfer objects, with no behaviour at all. Any field validation should be done inside the service operation (which is good security practice anyway). However, if you really want to bake this validation into the data contract class, then you can do so with OnDeserializingAttribute.