Today I played a little bit with WCF services. Currently in system we have extremely cumbersome and large object graphs.
Long story short we have Order data contract that always has Customer property in it. From other side Customer did not have reference to Order, so today I added property IList<Order> Orders to my customer and to test just called wcftestclient. Of course if you are not new to WCF, you know that it showed me that I probably have cyclic dependencies.

Reason is that if you have cyclic dependencies XmlSerializer anyway tries to serialize whole graph till it reaches stackoverflow.
[EDITED because of complains]  Actual error is something like “SerializationException: Customer contains cycles and cannot be serialized if reference tracking is disabled…

To avoid this problem you would need to mark you data contract with [DataContract(IsReference = true)]. So I did:

 [ServiceContract]
 public interface IDataContractCycles
 {
  [OperationContract]
  CustomerModel GetCustomer();
 }

 [DataContract(IsReference = true)]
 public class CustomerModel
 {
  [DataMember]
  public string Name { get; set; }

  [DataMember]
  public IList Orders { get; set; }
 }

 [DataContract(IsReference = true)]
 public class OrderModel
 {
  [DataMember]
  public string Name { get; set; }

  [DataMember]
  public CustomerModel Customer { get; set; }
 }

And to test I again fired up wcftestclient and it crashed!!! wtf? I had to commit some code to keep guys working so I commented Customer property of my Order. I thought that it would crash in our client the same way.

image

And now meaning of this story: TRUST NO ONE!

I cannot give up that easily, so I wrote ever simple wcf server and client, then added custom message inspector to see actual payload.

For this you would need something like below:

 class Program
 {
  static void Main(string[] args)
  {
   var client = new DataContractCyclesClient();

   client.Endpoint.Behaviors.Add(new CustomEndpointBehavior());

   client.Open();

   CustomerModel customerModel = client.GetCustomer();
  }
 }

 internal class CustomEndpointBehavior : IEndpointBehavior
 {
  public void Validate(ServiceEndpoint endpoint){}
  public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}
  public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){}

  public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  {
   clientRuntime.MessageInspectors.Add(new CustomMessageInspector());
  }
 }

 internal class CustomMessageInspector : IClientMessageInspector
 {
  public object BeforeSendRequest(ref Message request, IClientChannel channel)
  {
   return null;
  }

  public void AfterReceiveReply(ref Message reply, object correlationState)
  {
   reply.ToString(); // THIS IS ACTUAL PAYLOAD OF THE MESSAGE
  }
 }

I got correct object graph and payload with reference Id-s. See message below. It has Customer marked with z:Ref=”i1″ and then the same Id is used in two orders. This is cool stuff.

<s:Body u:Id=”_0″>

  <GetCustomerResponse xmlns=”http://tempuri.org/”>

    <GetCustomerResult z:Id=”i1″ xmlns:a=”http://schemas.datacontract.org/2004/07/WcfLearningCycles” xmlns:i=”http://www.w3.org/2001/XMLSchema-instance” xmlns:z=”http://schemas.microsoft.com/2003/10/Serialization/”>

      <a:Name>Andriy Buday</a:Name>

      <a:Orders>

        <a:OrderModel z:Id=”i2″>

          <a:Customer z:Ref=”i1″ />

          <a:Name>Kindle</a:Name>

        </a:OrderModel>

        <a:OrderModel z:Id=”i3″>

          <a:Customer z:Ref=”i1″ />

          <a:Name>HTC Cable</a:Name>

        </a:OrderModel>

      </a:Orders>

    </GetCustomerResult>

  </GetCustomerResponse>

</s:Body>

Liked it? Yes? No?

If you haven't subsribed yet, you can subsribe below: