WCF meet WSDL, WSDL meet WTF?

When developing enterprise SOA applications you will come accross the term: Contract First Development / Contract First Design.  If you haven't heard of this term it is the act of creating your contracts (WSDL, XSD, Schemas etc.) before touching code. When working with multiple parties and connected systems this approach is the best approach because it forces everyone to use the same rules.

I work a lot with HL7 WSDL's. These WSDL's allow my .NET development team to create and connect to webservices based off of these WSDLs using svcutil.exe or adding the WSDL as a service reference.    Contract-First is the "best practice" for this as it forces both the producer and consumer of the service to follow the same set of rules, and both know what is needed and what is expected.... What I didn't expect is what WCF would do with my WSDL's.

I recently had an issue where my WCF services were sending out bad XML to our connected partners in our development environment.  The fix was an easy one liner, but it took me awhile to figure it out.

When generating a WCF service based off of a WSDL you need to pay attention to serializer that .NET will use for your WCF service. By default .NET will use the DataContractSerializer as the default serializer.  This causes issues if you have attributes in your schema becuase DataContractSerializer doesn't not support them and when you're working with SOAP web services where a consumer is not a .NET system you will run into issues.

Common output from a HL7 SOAP web service:

<GetDemographics xmlns="urn:hl7-org:v3">
    <id root="2.16.840.1.113883.3.51.1.1.1" extension="404002d6-3978-413d-a49d-b11e240bbf26" />
    <creationTime value="20150204185920" />
    <versionCode code="V3PR1" />
    <interactionId root="2.16.840.1.113883.3.51.1.1.2" extension="HCIM_IN_GetDemographicsResponse" />
    <processingCode code="P" />
    <processingModeCode code="T" />
    <acceptAckCode code="NE" />
    <receiver typeCode="RCV">

What WCF Generates when using the DataContractSerializer on the same service:

<GetDemographicsResponse xmlns="urn:hl7-org:v3" xmlns:a="http://schemas.datacontract.org/2004/07/HCIM.HL7v3.Objects.QUPA_AR101102" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <a:acceptAckCode>
        <a:nullFlavor>NI</a:nullFlavor>
        <a:nullFlavorSpecified>false</a:nullFlavorSpecified>
        <a:code>NE</a:code>
        <a:codeSystem i:nil="true" />
        <a:codeSystemName i:nil="true" />
        <a:codeSystemVersion i:nil="true" />
        <a:displayName i:nil="true" />
        <a:originalText i:nil="true" />
        <a:qualifier i:nil="true" />
        <a:translation i:nil="true" />
    </a:acceptAckCode>   
    <a:creationTime>
        <a:nullFlavor>NI</a:nullFlavor>
        <a:nullFlavorSpecified>false</a:nullFlavorSpecified>
        <a:value>20150204185920</a:value>
    </a:creationTime>

The issue is the Serializer that is being used, and it's also an easy fix.

When generating WCF services from a WSDL you'll see something similar:

[ServiceContract(Namespace = "urn:hl7-org:v3")]
public interface IPRPA_AR101202
{
    [OperationContract(Name = "PersonRevised", Action = "urn:hl7-org:v3/PRPA_IN101204")]
    PersonRevisedResponse1 PersonRevised(Message request);
}

The fix to allow WCF to send valid SOAP Xml is a one liner:

[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults = true)]

Add this to the interface :

[ServiceContract(Namespace = "urn:hl7-org:v3")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults = true)]
public interface IPRPA_AR101202
{
   [OperationContract(Name = "PersonRevised", Action = "urn:hl7-org:v3/PRPA_IN101204")]
   PersonRevisedResponse1 PersonRevised(Message request);
}

Once this is in place, WCF will generate valid SOAP XML. This attribute is require for both distributing and recieving SOAP XML messages through WCF.

No Comments