09 Mar 2009

A month ago, I wrote an post on how to send an XmlDocument trough Windows Communication Foundation. At the end of the post, I noted that the ToString method on the Message object might return "...stream..." instead of the SOAP content.

A recent development needed to have the full message content in a string, and we hit the "...stream..." issue again. The development was a message inspector which was to be used on various BizTalk WCF Adapters. The inspector had to work on service side and on client side, implementing AfterReceiveRequest of IDispatchMessageInspector and BeforeSendRequest of IClientMessageInspector. Strangely, Message.ToString returned the full message on the server side adapter, but returned "...stream..." in client side adapter.

Searching on that matter on Google didn't provided me with lots of results, but I found a post with a solution to that particular issue: "Accessing the message inside of your WCF service operations". So, it seems that this is due to the fact that the Message object is streamed (yeah I know it's quite obvious...) on the client side.

So, to get the message's content as a string, here is what is needed:

StringWriter stringWriter = new StringWriter();
XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);

String messageContent = stringWriter.ToString();

However, there is an issue with that code in a message inspector. Using it like this will throw some exception about the message already been written. According to the documentation, a Message object can only be read or written once:

As the body of a Message object is a stream, it can only be read or written once. This is enforced by maintaining the current state of the Message object. A Message object can be read/written/copied when in the Created state. Other states are Read, Written and Copied, which means that the respective action has been performed already once.

So, when calling Message.WriteMessage to write the message in the XmlTextWriter, the state of the message changes from "Created" to "Written". This operation is irreversible, and the message is pretty useless afterward.

To avoid this, we can make use of the MessageBuffer class. This can be used to make a copy of a message in a buffer, then you can use the object created to create as many Messages as you want. Here is how to use it:

MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
Message message = buffer.CreateMessage();

After that, the message variable holds a reference to a copy of the initial request, and the buffer object can be used to produce copies of the initial Message.

There is one last trick. When creating a MessageBuffer out of the request, we actually read the request Message object, changing its state to Read and making it unusable afterward (in this case, the application!). Notice that in IDispatchMessageInspector and IClientMessageInspector methods, Messages parameters are always given using the ref keyword, meaning that the reference can be modified.

Solution to that last issue is to make another copy of the initial request and assign it to the variable. Here's how to do it:

MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
Message message = buffer.CreateMessage();
//Assign a copy to the ref received
request = buffer.CreateMessage();

Now the request variable hold a reference to a fresh Message, and the message variable holds a reference to another copy of the Message object that you can mess with in your inspector.

blog comments powered by Disqus