Monday, December 17, 2012

How to add WCF client Message Header without using Operation Context

Introduction

In WCF when we want to set message header for service by WCF client, we normally use operation scope. Following is the example of one such implementation.

using (OperationContextScope scope = new OperationContextScope(wcfClient.InnerChannel))
      {
        MessageHeader header
          = MessageHeader.CreateHeader(
          "Service-Bound-CustomHeader",
          "http://Microsoft.WCF.Documentation",
          "Custom Happy Value."
          );
        OperationContext.Current.OutgoingMessageHeaders.Add(header);

        // Making calls.
        Console.WriteLine("Enter the greeting to send: ");
        string greeting = Console.ReadLine();

        //Console.ReadLine();
        header = MessageHeader.CreateHeader(
            "Service-Bound-OneWayHeader",
            "http://Microsoft.WCF.Documentation",
            "Different Happy Value."
          );
        OperationContext.Current.OutgoingMessageHeaders.Add(header);
(NOTE: The above code example has been taken from msdn at: http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontextscope.aspx)

This works great in simple scenarios where you can use the OperationContextScope object. But what if you cannot use the OperationContext or OperationContextScope object?

Detail

I recently ran into a scenario where I was using FactoryChannel to create client channel and send that object to sub classes that were using the channel. The problem came when I had to add a common message header to all the client proxies which were using my base class. I could have used the same code scheme that is mentioned in the above example. But the problem arose when I found out that there were hundreds of  client proxy(s) that needed to be changed. That solution was not feasible given time constraints and the mere fact that if I need to change anything again on the newly created headers then I had do to repeat the same process again for all the client proxies.

I had to find a solution where I can add the message headers at one place and do not have to change on all the client proxies. Luckily, I was using a service proxy base class which was initiating a channel for all the client proxies. Naturally, I just needed to update that proxy with a message header that I need. But that is where I hit the major road block.

The problem is that conventionally message headers are added using the OperationContextScope object. But on my base class there is no OperationContext object available to me.

The solution to the above mentioned issue comes by using good old Endpoint Behavior object.

I created an endpoint behavior where I declared a public property which carried my custom message header. On the service proxy base I declared an object for the newly created Endpoint Behavior and then added that to channel factory object.

Here is how it looked after the code modification:

   factory = new ChannelFactory();
   
    var endpointBehavior = new CustomBehaviorAttribute ();
    // Setting LoginToken as custom message header
    endpointBehavior.CustomMessageHeader = "Value";
    factory.Endpoint.Behaviors.Add(endpointBehavior);
   return factory.CreateChannel();



Here is the declaration of my behavior attribute:
    /// 
    /// An attribute that extends runtime behavior of operations so that user can
    /// attach the custom message inspector to client/service application.
    /// 
    [AttributeUsage(AttributeTargets.All)]     public class CustomBehaviorAttribute : AttributeIEndpointBehavior     {


        #region IEndpointBehavior Members
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            return;
        }
 
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {

            clientRuntime.MessageInspectors.Add(new MessageInspector(behavior.CustomMessageHeader));
        }
 
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            return;
        }
 
        public void Validate(ServiceEndpoint endpoint)
        {
            return;
        }
        #endregion
 
        #region Public Members
        /// 
        /// Gets or sets the custom message header.
        /// 
        ///          /// The custom message header.         ///          public string CustomMessageHeader { getset; }         
#endregion Public Members






The above code snippet shows how can you add a header without using OperationContext or OperationContextScope objects.