Welcome to MSDN Blogs Sign in | Join | Help

Contract Generation from WSDL/XML Schema - DataContractSerializer vs. XmlSerializer

WCF LOB Adapter SDK metadata object model generates WSDL / XML Schema with constructs that are well-liked by DataContractSerializer.  The Svcutil.Exe creates a proxy / service interface from this WSDL containing attributes such as ServiceContract, OperationContract, DataContract, DataMember, etc.  If the adapter developer provides own XML Schema structure in OperationMetadata and/or TypeMetadata derived class and the XML Schema contains constructs that are ignored or forbidden  by DataContractSerializer, the Svcutil.Exe and Add Adapter Service Reference Visual Studio Plug-In  falls back to use XmlSerializer for serialization.   

Following are some examples of forbidden XML Schema constructs:

·         Xsd:choice

·         Xsd:group

·         Xsd:any

·         Xsd:ref

·         Xsd:attribute

The metadata object model in WCF LOB Adapter SDK avoids generating any XML Schema constructs that are ignored and forbidden by DataContractSerializer.  But if the adapter developer wants to provide their own schema, then it is outside the Adapter SDK control and the WSDL will contain the XML Schema definitions as provided by the adapter developer.   The generate CLR proxy will have the XmlSerializer attributes defined on operations and its members.

What does this mean to the end-user? 

Adapter Consumers who want to use the adapter in BizTalk Server are not affected with XML/CLR (de) serialization, as they are working directly with XML Schema types.  As long as the XML message conforms to the XML Schema, the adapter understands it.   

But if the same adapter is also now being used within a .NET application that will convert the WSDL / XML Schema into relevant CLR objects, the end-users who are working with the generated CLR object may contain definitions that are “bizarre”. 

Let me give you an example.   Consider an XML Schema with optional elements defined (i.e. minOccurs=’0’).   When using DataContractSerializer, the minOccurs should map to IsRequired attribute in the CLR object.   The XmlSerializer on the other hand generates a proxy that contains the xxxSpecified properties for each optional element.  The xxxSpecified was a pain for our customers in .NET 2.0 when used with xsd.exe or wsdl.exe.  Now, let’s add another complex type in the WSDL that uses a forbidden construct such as “xsd:attribute”.  The Svcutil.Exe will revert to use XmlSerializer and generate an output that contains xxxSpecified fields.

See the sample code that illustrates the above point.

Sample WSDL (without any forbidden construct)

 

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions xmlns:tns="hello://Microsoft.WCF.Samples.Adapters" xmlns:doc="http://schemas.microsoft.com/servicemodel/adapters/metadata/documentation" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:ns2="hello://Microsoft.WCF.Samples.Adapters" targetNamespace="hello://Microsoft.WCF.Samples.Adapters" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <wsdl:types>

    <schema elementFormDefault="qualified" targetNamespace="hello://Microsoft.WCF.Samples.Adapters" version="1.0" xmlns="http://www.w3.org/2001/XMLSchema">

      <complexType name="MyType">

        <sequence>

          <element minOccurs="0" maxOccurs="1" nillable="true" type="double" name="ONillableDoubleField" />

          <element minOccurs="1" maxOccurs="1" nillable="true" type="double" name="MNillableDoubleField" />

          <element minOccurs="1" maxOccurs="1" type="double" name="MandatoryDoubleField" />

          <element minOccurs="0" maxOccurs="1" type="double" name="OptDoubleField" />

        </sequence>

      </complexType>

      <element name="SayHelloWorld">

        <annotation>

          <documentation>

            <doc:action>Hello/SayHelloWorld</doc:action>

          </documentation>

        </annotation>

        <complexType>

          <sequence>

            <element minOccurs="1" maxOccurs="1" name="inName" nillable="true" type="string">

              <annotation>

                <documentation>Hello World is said by this name.</documentation>

              </annotation>

            </element>

            <element minOccurs="0" maxOccurs="1" name="OptionalElement" type="dateTime" />

            <element minOccurs="0" maxOccurs="1" name="Container" type="ns2:MyType"/>

          </sequence>

        </complexType>

      </element>

      <element name="SayHelloWorldResponse">

        <annotation>

          <documentation>

            <doc:action>Hello/SayHelloWorld/response</doc:action>

          </documentation>

        </annotation>

        <complexType>

          <sequence>

            <element minOccurs="1" maxOccurs="1" name="SayHelloWorldResult" nillable="true" type="string" />

          </sequence>

        </complexType>

      </element>

    </schema>

  </wsdl:types>

  <wsdl:message name="HelloWorld_SayHelloWorld_InputMessage">

    <wsdl:part name="parameters" element="ns2:SayHelloWorld" />

  </wsdl:message>

  <wsdl:message name="HelloWorld_SayHelloWorld_OutputMessage">

    <wsdl:part name="parameters" element="ns2:SayHelloWorldResponse" />

  </wsdl:message>

  <wsdl:portType name="HelloWorld">

    <wsdl:operation name="SayHelloWorld">

      <wsdl:input wsaw:Action="Hello/SayHelloWorld" message="ns2:HelloWorld_SayHelloWorld_InputMessage" />

      <wsdl:output wsaw:Action="Hello/SayHelloWorld/response" message="ns2:HelloWorld_SayHelloWorld_OutputMessage" />

    </wsdl:operation>

  </wsdl:portType>

</wsdl:definitions>

SVCUTIL generated service interface proxy

svcutil HelloWorld.wsdl /out:HelloWorld.DataContractSereializer.cs

[assembly: System.Runtime.Serialization.ContractNamespaceAttribute("hello://Microsoft.WCF.Samples.Adapters", ClrNamespace="microsoft.wcf.samples.adapters")]

 

namespace microsoft.wcf.samples.adapters

{

    using System.Runtime.Serialization;

   

   

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

    [System.Runtime.Serialization.DataContractAttribute()]

    public partial class MyType : object, System.Runtime.Serialization.IExtensibleDataObject

    {

       

        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

       

        private System.Nullable<double> ONillableDoubleFieldField;

       

        private System.Nullable<double> MNillableDoubleFieldField;

       

        private double MandatoryDoubleFieldField;

       

        private double OptDoubleFieldField;

       

        public System.Runtime.Serialization.ExtensionDataObject ExtensionData

        {

            get

            {

                return this.extensionDataField;

            }

            set

            {

                this.extensionDataField = value;

            }

        }

       

        [System.Runtime.Serialization.DataMemberAttribute()]

        public System.Nullable<double> ONillableDoubleField

        {

            get

            {

                return this.ONillableDoubleFieldField;

            }

            set

            {

                this.ONillableDoubleFieldField = value;

            }

        }

       

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=1)]

        public System.Nullable<double> MNillableDoubleField

        {

            get

            {

                return this.MNillableDoubleFieldField;

            }

            set

            {

                this.MNillableDoubleFieldField = value;

            }

        }

       

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=2)]

        public double MandatoryDoubleField

        {

            get

            {

                return this.MandatoryDoubleFieldField;

            }

            set

            {

                this.MandatoryDoubleFieldField = value;

            }

        }

       

        [System.Runtime.Serialization.DataMemberAttribute(Order=3)]

        public double OptDoubleField

        {

            get

            {

                return this.OptDoubleFieldField;

            }

            set

            {

                this.OptDoubleFieldField = value;

            }

        }

    }

}

 

 

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

[System.ServiceModel.ServiceContractAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", ConfigurationName="HelloWorld")]

public interface HelloWorld

{

   

    [System.ServiceModel.OperationContractAttribute(Action="Hello/SayHelloWorld", ReplyAction="Hello/SayHelloWorld/response")]

    string SayHelloWorld(string inName, System.DateTime OptionalElement, microsoft.wcf.samples.adapters.MyType Container);

}

 

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

public interface HelloWorldChannel : HelloWorld, System.ServiceModel.IClientChannel

{

}

 

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

public partial class HelloWorldClient : System.ServiceModel.ClientBase<HelloWorld>, HelloWorld

{

   

    public HelloWorldClient()

    {

    }

   

    public HelloWorldClient(string endpointConfigurationName) :

            base(endpointConfigurationName)

    {

    }

   

    public HelloWorldClient(string endpointConfigurationName, string remoteAddress) :

            base(endpointConfigurationName, remoteAddress)

    {

    }

   

    public HelloWorldClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :

            base(endpointConfigurationName, remoteAddress)

    {

    }

   

    public HelloWorldClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :

            base(binding, remoteAddress)

    {

    }

   

    public string SayHelloWorld(string inName, System.DateTime OptionalElement, microsoft.wcf.samples.adapters.MyType Container)

    {