10 Feb 2009

Today, I was stuck with a very simple problem. I wanted to send some XML to as service, using WCF. The proxy generated using svcutil left me with a function accepting an object parameter and returning an object parameter. The service is some kind of echo, simply returning what you sent. I had a sample XML file that contained the message that the service expects to test it with.

I first tested the service by sending the sample as a String. The response object was an XmlDocument. I figured that if what I received was an XmlDocument, I could simply send one as input. Wrong. XmlDocument happens to be not serializable, great. I tired to use XmlDocument.DocumentElement instead, which is of type XmlElement, without any success either.

I then tested XDocument and XElement, but that didn't work either. Gosh.

Next attempt. I created a schema out of the XML sample file (using Visual Studio 2008, it is very easy. Open the XML file then in the menu, go for "XML" then "Create Schema"). I then used xsd tool to generate a serializable class that could hold the XML, and deserialized the sample file into an instance of that class to give it as to the client's function. Failure again.

VisualStudioXmlCreateSchema

Now, I was left with only one option, at least from what I could see: directly use the Message class from WCF.

I (re) found an article about Message on the excellent Service Station web site (written by Aaron Skonnard who writes amazing articles and records amazing Webcasts). I didn't want to go that way at the beginning, but I was left with only that.

First, the code has to be changed. Let's see how to invoke a service using the generic IRequestChannel interface.

var doc = new XmlDocument();
doc.LoadXml(xmlContent);
var message = Message.CreateMessage(MessageVersion.Soap11, "urn:someRequest", new XmlNodeReader(doc));

var factory = new ChannelFactory<IRequestChannel>("serviceHttpSoap11Endpoint");
var channel = factory.CreateChannel();

var response = channel.Request(message);

channel.Close();

So, that's pretty easy. Basically, the first step is to create a Message object, and give it a reader to the XmlDocument. I then created a ChannelFactory using the endpoint configuration generated by the previous client. Then I create a channel and use it to sent the message a built with the XmlDocument. Nifty!

But wait, there is one thing that is left to do. In the configuration file, the endpoint still uses the client generated contract, while in the code we use IRequestChannel as the contract. Also, the contract needs the full namespace of the contract. Here is the endpoint's configuration:

<endpoint address="http://foo.com/bar/service"
          binding="basicHttpBinding"
          contract="System.ServiceModel.Channels.IRequestChannel" name="serviceHttpSoap11Endpoint" />

This one worked, at last. I received a nice reply from the service, however not the one I expected, but this another story and is for my Java colleague to solve... heh

One last remark: strangely, if you set up your client to log the messages sent, the message logged at Service Level will not display the content of the Message body element. Instead, it will simply contain a "... stream ..." text.

XmlTrace

However, if you log the message at Transport Level, you will see the full content.



blog comments powered by Disqus