Sending XML data through WCF using System.ServiceModel.Channels.Message without using DataContractSerializer.

WCF - Windows Communication Foundation is a beautiful enterprise ready framework for developing service-oriented applications in .NET. They handle Synchronous and Asynchronous messages from one endpoint to another. 

WCF, by default, uses the DataContract serializer, but does support the following:

  • XmlSerializer
  • DataContractSerializer
  • NetDataContractSerializer

In this blog post I want to quickly discuss how to send a message to a WCF service, without using the DataContractSerializer.  Unlike XmlSerializer, the DataContractSerializer does not support xml Attributes:

The DataContractSerializer does not support the programming model used by the XmlSerializer and ASP.NET Web services. In particular, it does not support attributes like XmlElementAttribute and XmlAttributeAttribute. To enable support for this programming model, WCF must be switched to use the XmlSerializer instead of the DataContractSerializer.

I wrote a blog post awhile ago about how accomplish creating WCF services that use the XMLSerializer. In this post I will discuss how to send data to a SOAP Service while maintaining the integrity of the XML.

Some common code for wiring up a call to a WCF service is the following:

var channelFactory = new ChannelFactory<IPRPA_AR101002>(basicHttpBinding, endpointAddress);
 
channelFactory.Credentials.ClientCertificate.Certificate = new X509Certificate2(cert);
 
IPRPA_AR101002 channel = channelFactory.CreateChannel();
((IContextChannel)channel).OperationTimeout = new TimeSpan(0, 0, 0, 0, timeOutMs); 
 
var request = System.ServiceModel.Channels.Message.CreateMessage(MessageVersion.Soap11, SoapAction, ObjectToSend);
 
MCCI_IN000002 response = channel.HCIM_IN_PersonMerged(request).MCCI_IN000002;
 
((IClientChannel)channel).Close();

This will quickly wire up a request to a WCF service. The drawback is your allowing .NET to have full control over how to serialize the data you're sending. Looking at the Message.CreateMessage code you'll see: 

static public Message CreateMessage(MessageVersion version, string action, object body)
        {
            return CreateMessage(version, action, body, DataContractSerializerDefaults.CreateSerializer(GetObjectType(body), int.MaxValue/*maxItems*/));
        }

This will destroy any XML Attributes you have in your XML.  To get around this we must pass in the XML we want to use, not the object, essentially we want to use:

static public Message CreateMessage(MessageVersion version, string action, XmlReader body)
        {
            return CreateMessage(version, action, XmlDictionaryReader.CreateDictionaryReader(body));
        }

To do this, we need to serialize the object to a reader, the following will do it quite nicely:

Snippet

XmlSerializer xs = new XmlSerializer(message.GetType());
           using (StringWriter sw = new StringWriter())
           { 
               xs.Serialize(sw, message);
 
               using (var reader = XmlReader.Create(new System.IO.StringReader(sw.ToString())))
               { 
                   var request = System.ServiceModel.Channels.Message.CreateMessage(MessageVersion.Soap11, SoapAction, reader);
 
                   MCCI_IN000002 response = channel.HCIM_IN_PersonMerged(request).MCCI_IN000002;
 
                   ((IClientChannel)channel).Close(); 
 
                   return response; 
               }
           }

And voila, your server will now have a properly formed SOAP XML message sent through WCF.

No Comments