I Love Easy Extensibility

3 commentsWritten on March 10th, 2009 by
Categories: WCF

Welcome to episode 245 in my Love/Hate relationship with WCF. Today i had to add some technical logging to some infrastructure code. More specifically, we wanted to log the size of incoming and outgoing SOAP messages. If you're using something like the Request/Response service layer you do want to keep an eye on the size of those SOAP messages to make sure nobody is going overboard with the WCF batching.

I obviously already had my service, so i just needed something that i could plug in at the appropriate moment to record the size of incoming and outgoing SOAP messages. Turns out this was extremely easy to do. First, you need to write an inspector for the messages (which has to implement WCF's IDispatchMessageInspector interface):

    public class MessageInspector : IDispatchMessageInspector

    {

        private readonly ILog logger = LogManager.GetLogger(typeof(MessageInspector));

 

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)

        {

            if (logger.IsInfoEnabled)

            {

                logger.Info(string.Format("request message size: ~{0} KB", GetMessageLengthInKB(request)));

            }

 

            return null;

        }

 

        public void BeforeSendReply(ref Message reply, object correlationState)

        {

            if (logger.IsInfoEnabled)

            {

                logger.Info(string.Format("response message size: ~{0} KB", GetMessageLengthInKB(reply)));

            }

        }

 

        private static double GetMessageLengthInKB(Message message)

        {

            return Math.Round(Encoding.UTF8.GetBytes(message.ToString()).Length / 1024d, 2);

        }

    }

After that, you need a way to inject the MessageInspector into the behavior of your service. So you need to define your own behavior first:

    public class AddMessageInspectorBehaviorAttribute : Attribute, IServiceBehavior

    {

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {}

 

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,

            Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {}

 

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

        {

            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)

            {

                foreach (var endpoint in dispatcher.Endpoints)

                {

                    endpoint.DispatchRuntime.MessageInspectors.Add(new MessageInspector());

                }

            }

        }

    }

And then you apply that to your service:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

    [AddMessageInspectorBehavior]

    public class WcfRequestProcessor : IRequestProcessor

    {

        // the service stuff...

    }

And that was it! I was afraid i was going to have to figure out which one of WCF's 12.4 billion configuration options would make this possible but this actually didn't require any configuration at all. Which is very nice, IMO.

On a side note, if anyone knows of a better way to calculate the real size of a SOAP message, please let me know :)