Monday, May 2, 2011

Passing complex type through WCF header,The simple always work version.

Recently I tried to find a nice code sample for how to pass complex type through WCF, inside the header, meaning I want to send the client execution context to the server side.

All of the samples passes strings or uses custom header that needs to write and read the object manually, I didn't like it.

So I wrote one and it's working!
All you need is 2 classes, and one is just a factory for the other, very simple:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;

namespace debugniv.ComplexTypeOverWcf
{
  class HeaderHandler : IDispatchMessageInspector, IClientMessageInspector
  {
    #region Implementation of IDispatchMessageInspector

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
      // Read the complex type from header
      var headerIndex = request.Headers.FindHeader(typeof(MyHeader).Name, typeof(MyHeader).Namespace);

      if (headerIndex >= 0) // found!
      {
        var serializer = GenerateDataContractSerializer<MyHeader>();
        var myHeader = request.Headers.GetHeader<MyHeader>(headerIndex, serializer);
        // here you can do something with your header like push it to a global per request context.
      }
      return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
      // nothing here
    }

    #endregion

    #region Implementation of IClientMessageInspector

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
      var myHeader = GetMyHeader();
      request.Headers.Add(myHeader);
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
      // nothing to do here
    }

    #endregion

    private DataContractSerializer GenerateDataContractSerializer<T>()
    {
      return new DataContractSerializer(typeof(T), typeof(T).Name, typeof(T).Namespace, GetKnownTypes());
    }

    private IEnumerable<Type> GetKnownTypes()
    {
      // here return all your types inside the header
      yield return typeof(MyHeader);
      yield return typeof(SubType);
    }

    private MessageHeader GetMyHeader()
    {
      var data = new MyHeader();
      var serializer = GenerateDataContractSerializer<MyHeader>();
      return MessageHeader.CreateHeader(data.GetType().Name, data.GetType().Namespace, data, serializer);
    }


  }

  // this is the header 
  internal class MyHeader
  {
    public SubType InternalData { get; set; }
  }

  internal class SubType
  {
  }
}


The second thing you'll need is to attach this stuff to your service and proxy, here is one way to do this

using System;
using System.ServiceModel.Configuration;

namespace debugniv.ComplexTypeOverWcf
{
  public class HeaderExtensionElement : BehaviorExtensionElement
  {

    public override Type BehaviorType
    {
      get { return typeof (HeaderHandler); }
    }

    protected override object CreateBehavior()
    {
      return new HeaderHandler();
    }
  }
}

In you config file add the following