After some time ago noone could explain to me at tutorials.de how to log incoming and outgoing messages in WCF, I thought I should tell share my knowledge about that with you:
WCF offers a configuration for MessageLogging by default. These are the so called "Diagnostics" and absolutely sufficient if you're really just trying to log something. The related article about that you can find at the MSDN.
Unfortunately the logging can't be configured as detailed as I needed it at that time. My task was to store the sent message related to an object, to check wheter the preprocessing wrapper does its job.
After some searching I found the way of the "Extensions". And that works as follows:
You can set the endpoint in the configuration and attach an Endpoint-Behavior::
<endpoint address="ADDRESS" binding="basicHttpBinding" behaviorConfiguration="AttachClientInspector" contract="CONTRACT" />
Therefore a "Behavior" named "AttachClientInspector" needs to be created (Of course you can chose the name yourself). This one would look somewhat look like this:
<endpointBehaviors>
<behavior name="AttachClientInspector">
<ClientInspector />
</behavior>
</endpointBehaviors>
If you're using Visual Studio 2005, just don't wonder why the "ClientInspector"-Tag is beeing marked with the description "...invalid child element". That's just because the VS2005 doesn't recognize the Extension itself. And that's the next step you have to do. The config section "System.ServiceModel" allows subsections named "Bindings", "Behaviors" and "Extensions". Here you can add your "BehaviorExtension":
<behaviorExtensions>
<add name="ClientInspector " type="CLASSNAME, ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
Of course the CLASSNAME has to point on a specific class, which is qualified for inspecting the message. To make sure that the class is accepted for this, you have to derive it from "System.ServiceModel.Configuration.BehaviorExtensionElement" AND implement "System.ServiceModel.Description.IEndpointBehavior". You have to derive from BehaviorExtensionElement, to use the class as an extension for the behavior. It requires the allocation of BehaviorType, where you can just return the current class via typeof and the method CreateBehavior, where you just have to return an instance of the current class.
public override Type BehaviorType
{
get { return typeof(CLASSNAME); }
}
protected override object CreateBehavior()
{
return new CLASSNAME();
}
On the other hand IEndpointBehavior provides the methods which are called, when the extension is executed at runtime. The interesting method for us should be: "ApplyClientBehavior". This method has a parameter "clientRuntime" of the same-named type. Here you can now attach the inspector as follows:
clientRuntime.MessageInspectors.Add(new CUSTOMINSPECTOR());
So... that's the key thing, to call our inspector as soon as the Endpoint is beeing used. Now there's only missing the implementation of the inspector itself. Therefore you have to create a class named CUSTOMINSPECTOR (the uppercase words are just placeholders to demonstrate the relations) and derive it from the type "System.ServiceModel.Dispatcher.IClientMessageInspector". This interface adds two methods. AfterReceiveReply and BeforeSendRequest. The second one awaits a return value of the type "object"... simply write "return null;" into this method and everything works fine.
If you now hit the compile button, everything should build without any problems. Now we have to add the implementation of the inspector. Therefore both methods have parameters of the type "System.ServiceModel.Channels.Message". In these parameters there's the real message contained.
If you're now trying to use that object, I guess it won't last a long time until you're getting stressed up, because the message-object is getting invalid, as soon as you read any information out of it. Cool, isn't it? ;-)
But you can solve that problem also very easily, because the message-objects contain a method called "CreateBufferedCopy". By calling that method you can create a "buffered copy" of the current message-object:
MessageBuffer bufferedMessage = request.CreateBufferedCopy(request.ToString().Length);
(Here's the message-object "request". In the "AfterReceiveReply" the object is called "reply".)
You can modify the "buffered copy" as often as you want and create a message object again after your analysis has finished:
request = bufferedMessage.CreateMessage();
(And as already told - again: The parameter of the reply-method is "reply". The object which is filled here, has to be the same as passed to the current method.)
All you have to know about it is - you've got several ways to analyze the content of the message:
1.) You can create a XPathNavigator with the method "CreateNavigator" out of the "bufferedMessage", with which you can analyze the xml itself. Over that you can access the content at any part of the XPathNavigator via its property "InnerXML". This one contains the content of the current node as a plaintext-string.
2.) You can also, as already said before, use ToString on the request object, which will return the content of the message (The envelope).
I wish a lot of fun with that and hope I could help you!