Derik Whittaker

Syndication

News


How to Intercept a WCF Message to track message size

As we are building out our mobile WCF endpoints we want to be able to track/trend the size of messages which are being sent to our phones.  We want to do this for multiple reasons 1) to trend our message sizes over time to make sure we are being good little developers 2) to be able to report and measure the amount of data we are sending to each of our mobile devices because bandwidth is not free.

I will say that my approach may just be ‘one way’ to accomplish this, but it works for me.

In order to accomplish task we will be extending WCF in multiple places and creating multiple files.  I will list out a quick list of the files then dive into each file a bit further.  In the end we will have a working solution which will be good enough to get you started.

WCF Extension points

  • CustomServiceHost => This extends ServiceHost
  • CustomServiceHostFactory => This extends ServiceHostFactory
  • MessageDiagnosticsInterceptor => This implements the IDispatchMessageInspector interface (used to grab the outbound message)
  • MessageDiagnosticsServiceBehavior => This implements the IServiceBehavior interface (used to setup the interceptor)

The way this will work is this.  We will need to create each of our 4 custom classes, but the glue that binds this all together is how we setup our endpoint to use the CustomServiceHostFactory.

Creating our Custom Service Host

public class CustomServiceHost : ServiceHost
{
    public CustomServiceHost()
    {
    }

    public CustomServiceHost( Type serviceType, params Uri[] baseAddresses )
        : base( serviceType, baseAddresses )
    {
    }

    protected override void OnOpening()
    {
        Description.Behaviors.Add( new MessageDiagnosticsServiceBehavior() );
            
        base.OnOpening();
    }
}

In the above code the only real important thing to notice is that we are adding a behavior during the OnOpening call.  We will define this behavior below.

Creating our Custom Service Host Factory

public class CustomServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses )
    {
        return new CustomServiceHost( serviceType, baseAddresses );
    }
}

In the code above we are implementing our Host Factory and what we are doing here is returning an instance of our custom host we defined above.

Creating our Message Diagnostics Interceptor

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

    public object AfterReceiveRequest( ref Message request, IClientChannel channel, InstanceContext instanceContext )
    {
        return null;
    }

    public void AfterReceiveReply( ref Message reply, object correlationState )
    {
            
    }

    public void BeforeSendReply( ref Message reply, object correlationState )
    {
        try
        {
            if ( !reply.IsFault && !reply.IsEmpty )
            {
                var messageBodyReader = reply.GetReaderAtBodyContents();
                var messageBody = messageBodyReader.ReadOuterXml();

                DetermineAndLogMessageDiagnostics( messageBody );
                RebuildMessage( ref reply, messageBody );

            }
                   
        }
        catch ( Exception e )
        {
            Debug.WriteLine( e );
        }
    }

    private void DetermineAndLogMessageDiagnostics( string messageBody )
    {
        double bodySizeInBytes = Encoding.UTF8.GetByteCount( messageBody );

        var asKiloBytes =  bodySizeInBytes / 1024;

        Debug.WriteLine( "Message size in KBytes {0}", asKiloBytes );
    }

    private static void RebuildMessage( ref Message message, string messageBody )
    {
        if ( message.IsFault || message.IsEmpty ) return;

        var bodyDoc = new XmlDocument();
        bodyDoc.LoadXml( messageBody );
                
        // Create new message 
        var replacementMessage = Message.CreateMessage( message.Version,
                                                        null,
                                                        new XmlNodeReader( bodyDoc.DocumentElement ) );

        replacementMessage.Headers.CopyHeadersFrom( message );

        foreach ( var propertyKey in message.Properties.Keys )
        {
            replacementMessage.Properties.Add( propertyKey, message.Properties[ propertyKey ] );
        }
                
        // Close the 

The above is the ‘hair’ part of all of our logic.  It is here that we actually get the message and can do something with it.  You will notice that in the method BeforeSendReply we are grabbing the body of the message by using the GetReaderAtBodyContent() method.  One thing to note here is that since this is going to return an XmlDictionaryReader you can only call this message ONE TIME.  In fact you should also notice that I am pulling out the message body and using this in the RebuildMessage method.  This rebuild method is needed because I accessed the XmlDictionaryReader, without this you will get exceptions.

The code where you would want to extend to do your own diagnostics would be in the DetermineAndLogMessageDiagnostics method.  Here is where you can do anything you want with the message.

Creating our Message Diagnostics Service Behavior

public class MessageDiagnosticsServiceBehavior : IServiceBehavior
{
    public void Validate( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
    {
            
    }

    public void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters )
    {
        return;
    }

    public void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
    {
        foreach ( ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers )
        {
            foreach ( EndpointDispatcher epDisp in chDisp.Endpoints )
            {
                epDisp.DispatchRuntime.MessageInspectors.Add( new MessageDiagnosticsInterceptor() );  
            }
        }
    }
}

The code above simply injects our custom inspector (defined above) into the runtime.

Setting up our WCF endpoint to use our Custom Service Host Factory

<%@ ServiceHost Language="C#" Debug="true" 
Factory="MessageDiagnosticsInterceptors.CustomServiceHostFactory"
Service="MessageDiagnosticsInterceptorHost.Service1" 
CodeBehind="Service1.svc.cs" %>

In order to use the customHostFactory you will need to provide an entry in your svc file which references that factory.  You will see this in the ‘Factory’ line

As you can see creating an interceptor in WCF is not hard, but it does take a few files and it does take some setup work. I hope this helps someone.

Till next time,


Posted 02-03-2011 12:10 PM by Derik Whittaker
Filed under: ,

[Advertisement]

Comments

air jordans wrote re: How to Intercept a WCF Message to track message size
on 02-11-2011 4:05 AM

There's only one corner of the universe you can be sure of improving, and that's your own self.

Andrew M wrote re: How to Intercept a WCF Message to track message size
on 02-16-2011 7:50 AM

I just want you to know that this, in fact, helped me. I thought my issue was message size, but that didn't make sense since I had maxReceivedMessageSize="2147483647".

Implementing this inspector lead me to determine that the real issue was that my MaxItemsInObjectGraph was too low. I never would have found it otherwise, as this property actually exists in the dataContractSerializer attribute in the binding's behavior.

So, thanks a lot for putting the effort into this tutorial. I greatly appreciate it.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)