Introduction to Windows Communication Foundation for the .NET Compact Framework Messaging Stack

This BLOG continues my discussion into new features of .NET Compact Framework 3.5 by diving into Windows Communication Foundation for the .NET Compact Framework Messaging stack.  To start I suggest you read Romans WCF overview BLOG at https://blogs.msdn.com/romanbat/archive/2006/10/21/windows-communication-foundation-compact-edition-and-the-story-of-the-lunch-launcher.aspx

As with all features included in NETCF, the WCF implementation needed to be small to fit into device ROM's.  Our original budget was 250kb and we tracked closely to this goal by cutting out the service layer and implementing a sub-set of the messaging stack.  Throughout our investigation a few critical end to end solutions required messaging level security, which was not orrginaly planned for.  To address these solutions, WCF for NETCF includes a subset of WS-Security.  This drove our size up to ~450kb.  The final implementation of WCF for NETCF includes the HTTP and email based transport, the text encoders and WS-Security.  Each of these features are extensible and  provides a flexible framework to allow each component to be swapped out or extended as needed. 

To introduce the messaging stack lets look at a hello world example using  HTTP or HTTPS request response.  There are distinct tasks which need to be completed for the client solution which includes; create the message, create the channel, open the channel, send the message then receive the response.   Server tasks follow similar steps; create the channel, open the channel and wait for response, parse and respond to the message.  To demonstrate this type of messaging channel I've added a simple code example below.  The client code will run on the PC so I if you do not have Orcas Beta1 or greater, which includes System.ServiceModel.dll, you can start your investigation PC them move to device when Orcas Beta2 is released.  Lets look at the specifics of a simple hello world example.

I've updated the code below to reflect the changes found in Orcas Beta2.

 Messaging Hello World Client Code:

The Hello World example below describes a simple use of the messaging stack in conjunction with the canned HTTP binding element.  

We need to implement infrastructure used by both the client and server.

First we need to add the needed references.

First add references to:

1. System.ServiceModel.dll

2. System.XML.dll

3 System.Runtime.Serialization.dll

Next we add the using statements.

 using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization; 

Next we need serializable class to contain our data. 

     [System.SerializableAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace = "https://Microsoft.ServiceModel.Samples")]
    public class TransmittedObject
    {
        [System.Xml.Serialization.XmlElementAttribute(Order = 0)]
        public string str;

        [System.Xml.Serialization.XmlElementAttribute(Order = 1)]
        public int i;
    } 

We next need to a helper class by extend the XMLSerilizer to be used in the context of WCF messages.

 public sealed class XmlSerializerWrapper : XmlObjectSerializer
    {
        XmlSerializer serializer;
        string defaultNS;
        Type objectType;

        public XmlSerializerWrapper(Type type)
            : this(type, null, null)
        {
        }

        public XmlSerializerWrapper(Type type, string name, string ns)
        {
            this.objectType = type;
            if (!String.IsNullOrEmpty(ns))
            {
                this.defaultNS = ns;
                this.serializer = new XmlSerializer(type, ns);
            }
            else
            {
                this.defaultNS = "";
                this.serializer = new XmlSerializer(type);
            }

        }

        public override bool IsStartObject(XmlDictionaryReader reader)
        {
            throw new NotImplementedException();
        }

        public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
        {
            throw new NotImplementedException();
        }
        public override void WriteEndObject(XmlDictionaryWriter writer)
        {
            throw new NotImplementedException();
        }

        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteObject(XmlDictionaryWriter writer, object graph)
        {
            this.serializer.Serialize(writer, graph);
        }

        public override object ReadObject(XmlDictionaryReader reader)
        {
            string readersNS;

            readersNS = (String.IsNullOrEmpty(reader.NamespaceURI)) ? "" : reader.NamespaceURI;
            if (String.Compare(this.defaultNS, readersNS) != 0)
            {
                this.serializer = new XmlSerializer(this.objectType, readersNS);
                this.defaultNS = readersNS;
            }

            return (this.serializer.Deserialize(reader));
        }
    } 

We now have enough infrastructure to build the message.  It can look something like:

             TransmittedObject to = new TransmittedObject();
            to.str = "Hello";
            to.i = 5;

            XmlSerializerWrapper wrapper = 
                new XmlSerializerWrapper(typeof(TransmittedObject));

            Message m = Message.CreateMessage
                (MessageVersion.Soap11, "urn:test", to, wrapper);

Next up we need to create the channel using the BasicHttpBinding.

             BasicHttpBinding binding = new BasicHttpBinding();

            BindingParameterCollection parameters = 
                new BindingParameterCollection();

            IChannelFactory<IRequestChannel> channelFactory = 
                binding.BuildChannelFactory<IRequestChannel>(parameters);

            channelFactory.Open();

The channel then needs to be opened.  For PC to PC you can use LocalHost. If your using a device, you will need to setup an IIS server with an appropriate address which is reachable by the device

             IRequestChannel outChannel = 
                channelFactory.CreateChannel
                (new EndpointAddress(new Uri("https://<<ServerAddress>>:<<PortNumber>>")));

            outChannel.Open(TimeSpan.MaxValue);

It time to send the message and wait for the reply

             Message reply = outChannel.Request(m, TimeSpan.MaxValue);

Once the reply is received do Deserialize it and do something with the data.

             TransmittedObject to1 = 
                reply.GetBody<TransmittedObject>
                (new XmlSerializerWrapper(typeof(TransmittedObject)));

            MessageBox.Show(to1.str + " " + to1.i.ToString());

Lets clean up

             outChannel.Close();
            channelFactory.Close();

Messaging Hello World Server Code:

Now lets look at what the server code could look like.   

The server needs all the infrastructure described above including reference and using statements, TransmittedObject and XmlSerializerWrapper

Next the channel needs to be built.

             BasicHttpBinding binding = new BasicHttpBinding();

            BindingParameterCollection parameters = 
                new BindingParameterCollection();

            IChannelListener<IReplyChannel> listener = 
                binding.BuildChannelListener<IReplyChannel>
                (new Uri("https://<<ServerAddress>>:<<PortNumber>>"), parameters);

            listener.Open();

            XmlSerializerWrapper wrapper = 
                new XmlSerializerWrapper(typeof(TransmittedObject));

Next the channel needs to be opened.

             IReplyChannel channel = listener.AcceptChannel();
            channel.Open(TimeSpan.MaxValue);

Receiving the message

             RequestContext r = 
                channel.ReceiveRequest(TimeSpan.MaxValue);

            TransmittedObject to = 
                r.RequestMessage.GetBody<TransmittedObject>(wrapper);

Process the message

             to.str = to.str + " World";
            to.i = to.i + 1;

Finally we create the message and send the response.

             Message m = Message.CreateMessage
                (MessageVersion.Soap11, "urn:test", to, wrapper);

            r.Reply(m, TimeSpan.MaxValue);

Lets Clean up

 channel.Close();

I hope this provides a brief description of using the WCF messaging stack as its implemented for the device using the .NET Compact Framework.