The WCF MessageEncoder Decoded

If you've read my earlier articles, you're now familiar with many fundamental aspects of WCF. My first article, "A Primer to Windows Communication Foundation", presented an overview of WCF. My second article, "Building WCF Channels and Bindings", explored Channel construction. In this article, you're going to look at the role and design of a MessageEncoder.

The MessageEncoder

MessageEncoders typically inhabit Transport Channels, although MessageEncoders can be used elsewhere in the Channel Stack. Often, a MessageEncoder's sole purpose is to turn the bytes coming over the wire into a WCF Message Class representation.

Typically, bytes on the wire are a Multipurpose Internet Mail Extensions (MIME) content type. MIME support is a WCF cornerstone. WCF is also built for SOAP-based interactions. In fact, WCF comes equipped to handle multiple SOAP versions and various forms of XML data. MessageEncoders shipping with WCF can build messages from various content and SOAP protocols.

So, with all this support for content and protocols, why would you build your own MessageEncoder?

Why Build Your Own?

As you may have guessed, not all data coming over the wire can be neatly categorized and packaged. Therefore, creating a Message class representation may require some custom handling. Custom handling may entail data decrypting/encrypting or may be supplemented by an additional source.

Other reasons for building your own MessageEncoder is tht you may want to utilize a custom Message class throughout your Channel Stack or you may want to apply a transformation to the incoming data as you create a Message class.

Whatever the case when writing your own MessageEncoder, you will probably apply the same general formula and toolset. Now, you're going to explore the sample code and I'm going to share the formula and toolset for building your own MessageEncoder.

Sample Overview

The sample opens an XML file using the XmlDocument class, translates the XmlDocument into bytes, creates a Message class from the bytes, and then copies the Message class back into another XmlDocument class.

There are some things to consider before you explore the sample.

Aside from illustrating the steps to take building your own MessageEncoder, the sample provides no real practical solution.

Normally, the MessageEncode is embedded inside of a WCF. As stated earlier, normally a MessageEncoder inhabits a Transport Channel class.

Finally, there are many ways to manipulate the bytes coming over the wire and to coerce the data into a Message class. Covering all tools and options is beyond the scope this article, so I'm going to review some of these tools with you before delving into the code.

Tools of the Trade

Manipulating XML data is the realm of the XmlReader and XmlWriter classes. XmlReaders and XmlWriters support all sorts of functionality including:

  • A separate settings class for checking conformance, including whitespace, and specifying a particular encoding
  • Reading, Writing, and efficiently navigating XML
  • Reading XML from Stream classes

XmlDictionalReader and XmlDictionaryWriter classes serve to generate XmlReaders and XmlWriters specifically supporting new features in WCF.

XslCompiledTransformation is a new .NET 2.0 implementation of the .NET Extensible Stylesheet Language Transformation (XSLT) transformations functionality. XSLT serves to transformation a particular XML document into some other representation.

MessageEncoders can work with large amounts of data. Allocating space for large pieces of data can create a bottleneck in an application. So, WCF utilizes a class called MessageBuffer to control and manage pre-allocated memory pools.

MessageEncoders work with .NET Streams. Streams are classes that manipulate a series of bytes residing inside various places (file system, memory, network). All streams have a common base class along with functions specific to their area of specialization.

For more details, see the .NET Framework documentation and the articles listed under Sources at the end of this article.

It's time to delve into the sample and put the tools above to use.

Class Requirements

Like Channels and other classes in WCF, when you build a MessageEncoder you also build an accompanying MessageEncodingBindingElement and MessageEncoderFactory. Also, as with other WCF classes, the binding class handles configuration and the factory class handles creation. Following is the class declaration for TestEncoderFactory.

public class TestEncoderFactory : MessageEncoderFactory
{

Following is the class declaration for TestEncoderBinding.

public class TestEncoderBindingElement :
   MessageEncodingBindingElement
{

As stated earlier, normally the MessageEncoder resides inside the Transport Channel. My WCF Channels and Binding article illustrates how to incorporate a MessageEncoder in the binding and use a MessageEncoder inside a Transport Channel. Typically, the MessageEncoder is created by using the following function calls and properties.

TestEncoderBindingElement elm = new TestEncoderBindingElement();
TestEncoderFactory factory    = new TestEncoderFactory(elm);
RunProgram prog               = new RunProgram();

   ..

prog.Run(factory.Encoder);

MessageEncoder is the base class for all MessageEncoders. MessageEncoder contains overridable versions of the overloaded ReadMessage and WriteMessage functions. Which functions you invoke depend on which style of Message you wish to create; see the Sources section at the end of the article for more information on Messages. You'll see how to implement a few ReadMessage and WriteMessage functions later in the article.

There are also some important overridable properties and functions dealing with the "body" or data portion of a Message class. ContentType, MediaType, and MessageVersion are the key properties. The importance of other functions depends on your implementation of the class.

As stated earlier, a MessageEncoder turns bytes on the wire to Message class. Look at how this is done.

Bytes to Messages

The following code loads a stream, copies the stream to a buffer using a buffer manager, and calls ReadMessage on the TestMessageEncoder to create a Message class.

buffer = mgr.TakeBuffer((int)streamFrom.Length);

streamFrom.Read(buffer, 0, (int)streamFrom.Length);

byteArray = new ArraySegment<byte>(buffer);

msg = _encoder.ReadMessage(byteArray, mgr);

The invoked ReadMessage follows.

public override Message ReadMessage(Stream stream,
   int maxSizeOfHeaders, string contentType)
{
   Message msg;
   XmlDocument doc = new XmlDocument();

   doc.Load(stream);

   TestBodyWriter bw = new TestBodyWriter(doc);

   msg = Message.CreateMessage(this.MessageVersion,"",bw);

   return (msg);
}

Your first question is probably what is this ArraySegment<byte> generic class? According to the .NET Framework documentation, an ArraySegment works in conjunction with the Array class, providing a "wrapper" around a portion of the underlying Array.

As you may have noticed, CreateMessage on the Message class provides many ways to build a message class. Reviewing all the overloaded CreateMessage variations is beyond the scope of this article.

Instead, I chose one of the BodyWriter class variations offering the most control over the contents of the "body" of the message. My BodyWriter implementation serves as a wrapper for the XmlDocument class. Your own implementation of the BodyWriter will likely use some of the .NET classes I described earlier in the article.

Next, you'll see how to change the Message class you created back to bytes.

Messages Back to Bytes

Variations of the WriteMessage function copy a Message class to bytes and streams. My implementation of the WriteMessage function appears below.

public override void WriteMessage(Message message, Stream stream)
{

   XmlWriter writer = XmlWriter.Create(stream);
   XmlDictionaryWriter xmlDW =
      XmlDictionaryWriter.CreateDictionaryWriter(writer);

   message.WriteBodyContents(xmlDW);
   xmlDW.Close();

}

The implementation is typical. I used the XmlDictionalWriter class to write the message body data to a stream. As the Message is written to the stream because you created the message with a BodyWriter class; the OnWriteBodyContents in the BodyWriter is invoked. As stated earlier, the BodyWriter is a wrapper for the XmlDocument class and therefore simply dumps contents from the XmlDocument to the XmlWriter parameter.

Conclusion

MessageEncoders convert the bytes coming over the wire to a WCF Message class. A MessageEncoder normally resides inside of a Transport Channel. Like many other WCF classes, MessageEncoders have an accompanying binding and factory class. Message encoders typically utilize XmlReaders, XmlWriters, and stream classes in the .NET Framework.

Sources



About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Downloads

Comments

  • WCF interoperatibility biztalk

    Posted by Venger on 10/10/2014 02:32am

    Hello exceltent post. But i have a question, how could be the implementation using biztalk wcf custom adapter with a custom encoder. I need to do a custom encrypt/decrypt. If you have any idea i will appreciated.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • It's no secret what keeps CIOs up at night. Mobile, cloud, data, security, and social have become the "five imperatives," the drivers of business progress, innovation, and competitive differentiation. Business leaders around the world want to hear how other companies are succeeding. How are they applying the latest technologies? How did they get started? What outcomes are they achieving? Read this online magazine for success stories from organizations like the NBA, Pfizer, and San Jose State University as they …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds