Tuesday, February 11, 2014

WCF Data Member Order

One element of SOAP in WCF that is often overlooked is Data Member order. We need to stop and remind ourselves from time to time that SOAP is just XML, and that a data contract is not actually code, but rather an abstraction of XML (or JSON). The WCF Test Client can actually give us a good illustration of this. When you have a operation that you are going to call, the standard view is "Formatted", like so:


But there is also an "XML" view, which gives you this:


This is what actually gets transmitted to the service; this is the 'real' message.

WCF can actually be very picky about the order of the elements in a SOAP message: if the message being sent and the service receiving it don't agree on the data member order, the message won't deserialize properly. Worse, it won't give you any errors, but your object will only be partially populated. Now, if you publish your service, the client generates the service reference, and no one ever changes either, then this isn't an problem. But services do change - even adding a new element where you can accept the default value can change message order expectation. And if the client is not using WCF on their end (e.g., because they're using Java, or are a legacy system), then agreement between client and service cannot be assumed.

The good news is you can control the order of elements in a message.

The code for the previous examples is this:

[DataContract]
public class DeleteClaimRequest
{
 [DataMember]
 public int ClaimId { get; set; }

 [DataMember]
 public string RequestorUserName { get; set; }
}

Right now, the data member order is merely alphabetical (note: not the order of declaration). But let's say we wanted to enforce that the RequestorUserName came first. We can do that by modifying the code like so:

[DataContract]
public class DeleteClaimRequest
{
 [DataMember(Order = 2)]
 public int ClaimId { get; set; }

 [DataMember(Order = 1)]
 public string RequestorUserName { get; set; }
}

The message would then be formatted like so:

<s:Envelope 
   xmlns:a="http://www.w3.org/2005/08/addressing" 
   xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">
       http://company.com/service/2/IDataReceiver/DeleteClaim
    </a:Action>
  </s:Header>
  <s:Body>
    <DeleteClaim xmlns="http://company.com/service/2">
      <message xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <RequestorUserName>john</RequestorUserName>
        <ClaimId>123</ClaimId>
      </message>
    </DeleteClaim>
  </s:Body>
</s:Envelope>

Easy, right? Yes, but only because there's no inheritance involved. Consider this contract:

[DataContract]
public class SetCredentialsRequest : ManagementRequest
{
 [DataMember(Order = 1)]
 public string ProviderUserID { get; set; }

 [DataMember(Order = 2)]
 public string ProviderPassword { get; set; }
}
 
[DataContract]
public class ManagementRequest
{
 [DataMember(Order = 3)]
 public string AssociateUsername { get; set; }
}

The result is not what you expect. WCF will actually want the message to be in this format:

<s:Envelope 
    xmlns:a="http://www.w3.org/2005/08/addressing" 
    xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">
       http://company.com/service/2/IServiceManager/SetProviderCredentials
    </a:Action>
    <a:MessageID>urn:uuid:3c9a3661-0c33-461e-adca-08076696742d</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
  </s:Header>
  <s:Body>
    <SetProviderCredentials xmlns="http://company.com/service/2">
      <request xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <AssociateUsername>john</AssociateUsername>
        <ProviderUserID>user123</ProviderUserID>
        <ProviderPassword>41aer45b1</ProviderPassword>
      </request>
    </SetProviderCredentials>
  </s:Body>
</s:Envelope>

This is because WCF always puts the elements from a base class first (see this MSDN article). No matter what value you put as Order for AssociateUsername, WCF will always expect it to be first. And just to make things to even more confusing, the "Formatted" view of the WCF Test Client will show the message like so:


The solution? Use interfaces!

If you change the code like so:

[DataContract]
public class SetCredentialsRequest : IManagementRequest
{
 [DataMember(Order = 1)]
 public string ProviderUserID { get; set; }

 [DataMember(Order = 2)]
 public string ProviderPassword { get; set; }

 [DataMember(Order = 3)]
 public string AssociateUsername { get; set; }
}

public interface IManagementRequest
{
 string AssociateUsername { get; set; }
}

then it will produce the desired result:

<s:Envelope 
     xmlns:a="http://www.w3.org/2005/08/addressing" 
     xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">
       http://company.com/service/2/IServiceManager/SetProviderCredentials
    </a:Action>
    <a:MessageID>urn:uuid:67ddfa83-88a7-407e-a4b9-12c314a05905</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
  </s:Header>
  <s:Body>
    <SetProviderCredentials xmlns="http://company.com/service/2">
      <request xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <ProviderUserID>user123</ProviderUserID>
        <ProviderPassword>41aer45b1</ProviderPassword>
        <AssociateUsername>john</AssociateUsername>
      </request>
    </SetProviderCredentials>
  </s:Body>
</s:Envelope>

C# doesn't care what order the members of an interface are placed on the inheriting object, and WCF serialization doesn't deal with a base interface the way it deals with a base class, so you are free to specify the order explicitly and definitely. This way you're able to achieve type unity/inheritance while still being able to specify the necessary order. And because the order is specified in the derived object instead of by the interface, AssociateUsername could have a different order in every class that inherits from IManagementRequest. (Generated service reference code is smaller and less complex as well.) As an added bonus, interfaces generally make for better inheritance chains, as a class can implement many interfaces but only one base class. (Base classes have their place for separation of concerns in code, but again data contracts are not code!)

So, long story short:
  • Always explicitly specify data member order to avoid serialization problems.
  • Use interfaces when creating data contract inheritance chains.
  • When you change a contract, be mindful of how member order changes may affect clients/impact message serialization.