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)

    {

        return base.Channel.SayHelloWorld(inName, OptionalElement, Container);

    }

}

 

Sample WSDL (with a forbidden construct)

 

Change the WSDL to include a DataContractSerializer forbidden construct within the complex type MyType.  Keep everything else as is. 

     <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>       

        <attribute default="X" name="xAttribute" type="string" />

      </complexType>

 

svcutil HelloWorld-xmltypes.wsdl /out:HelloWorld.XmlSerializer.cs

Check the impact of doing this on the generated proxy and notice other attributes such as –

·         XmlSerializerFormatAttribute

·         XmlTypeAttribute

·         XmlElementAttribute

·         XmlAttributeAttribute

·         MessageContractAttribute

·         MessageBodyMemberAttribute

 

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

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

public interface HelloWorld

{

   

    // CODEGEN: Parameter 'SayHelloWorldResult' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.

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

    [System.ServiceModel.XmlSerializerFormatAttribute()]

    SayHelloWorldResponse SayHelloWorld(SayHelloWorldRequest request);

}

 

/// <remarks/>

[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.30")]

[System.SerializableAttribute()]

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.ComponentModel.DesignerCategoryAttribute("code")]

[System.Xml.Serialization.XmlTypeAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters")]

public partial class MyType

{

   

    private System.Nullable<double> oNillableDoubleFieldField;

   

    private bool oNillableDoubleFieldFieldSpecified;

   

    private System.Nullable<double> mNillableDoubleFieldField;

   

    private double mandatoryDoubleFieldField;

   

    private double optDoubleFieldField;

   

    private bool optDoubleFieldFieldSpecified;

   

    private string xAttributeField;

   

    public MyType()

    {

        this.xAttributeField = "X";

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]

    public System.Nullable<double> ONillableDoubleField

    {

        get

        {

            return this.oNillableDoubleFieldField;

        }

        set

        {

            this.oNillableDoubleFieldField = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlIgnoreAttribute()]

    public bool ONillableDoubleFieldSpecified

    {

        get

        {

            return this.oNillableDoubleFieldFieldSpecified;

        }

        set

        {

            this.oNillableDoubleFieldFieldSpecified = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)]

    public System.Nullable<double> MNillableDoubleField

    {

        get

        {

            return this.mNillableDoubleFieldField;

        }

        set

        {

            this.mNillableDoubleFieldField = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlElementAttribute(Order=2)]

    public double MandatoryDoubleField

    {

        get

        {

            return this.mandatoryDoubleFieldField;

        }

        set

        {

            this.mandatoryDoubleFieldField = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlElementAttribute(Order=3)]

    public double OptDoubleField

    {

        get

        {

            return this.optDoubleFieldField;

        }

        set

        {

            this.optDoubleFieldField = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlIgnoreAttribute()]

    public bool OptDoubleFieldSpecified

    {

        get

        {

            return this.optDoubleFieldFieldSpecified;

        }

        set

        {

            this.optDoubleFieldFieldSpecified = value;

        }

    }

   

    /// <remarks/>

    [System.Xml.Serialization.XmlAttributeAttribute()]

    [System.ComponentModel.DefaultValueAttribute("X")]

    public string xAttribute

    {

        get

        {

            return this.xAttributeField;

        }

        set

        {

            this.xAttributeField = value;

        }

    }

}

 

[System.Diagnostics.DebuggerStepThroughAttribute()]

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

[System.ServiceModel.MessageContractAttribute(WrapperName="SayHelloWorld", WrapperNamespace="hello://Microsoft.WCF.Samples.Adapters", IsWrapped=true)]

public partial class SayHelloWorldRequest

{

   

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", Order=0)]

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]

    public string inName;

   

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", Order=1)]

    public System.DateTime OptionalElement;

   

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", Order=2)]

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]

    public MyType Container;

   

    public SayHelloWorldRequest()

    {

    }

   

    public SayHelloWorldRequest(string inName, System.DateTime OptionalElement, MyType Container)

    {

        this.inName = inName;

        this.OptionalElement = OptionalElement;

        this.Container = Container;

    }

}

 

[System.Diagnostics.DebuggerStepThroughAttribute()]

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

[System.ServiceModel.MessageContractAttribute(WrapperName="SayHelloWorldResponse", WrapperNamespace="hello://Microsoft.WCF.Samples.Adapters", IsWrapped=true)]

public partial class SayHelloWorldResponse

{

   

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", Order=0)]

    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]

    public string SayHelloWorldResult;

   

    public SayHelloWorldResponse()

    {

    }

   

    public SayHelloWorldResponse(string SayHelloWorldResult)

    {

        this.SayHelloWorldResult = SayHelloWorldResult;

    }

}

 

[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)

    {

    }

   

    SayHelloWorldResponse HelloWorld.SayHelloWorld(SayHelloWorldRequest request)

    {

        return base.Channel.SayHelloWorld(request);

    }

   

    public string SayHelloWorld(string inName, System.DateTime OptionalElement, MyType Container)

    {

        SayHelloWorldRequest inValue = new SayHelloWorldRequest();

        inValue.inName = inName;

        inValue.OptionalElement = OptionalElement;

        inValue.Container = Container;

        SayHelloWorldResponse retVal = ((HelloWorld)(this)).SayHelloWorld(inValue);

        return retVal.SayHelloWorldResult;

    }

}

 

Summary: Avoid, where possible, having constructs in the WSDL / XML Schema that can create a less optimum proxy for the end-user (applicable when using a top-down approach with creating a service interface from WSDL / XML Schema).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个WSDL文档,用于描述一个Web服务。其中: - xmlns:xsd、xmlns:wsdlxmlns:tns、xmlns:soap、xmlns:ns2、xmlns:ns1是命名空间定义。 - name属性指定了这个Web服务的名称为"CXFofTanJYService",targetNamespace属性指定了这个Web服务的命名空间为"http://dao.service.com/"。 - <wsdl:import>元素用于导入其他命名空间下的WSDL文档,location属性指定了这个WSDL文档的位置,namespace属性指定了这个WSDL文档的命名空间。 - <wsdl:binding>元素用于定义这个Web服务的具体绑定信息,name属性指定了绑定的名称为"CXFofTanJYServiceSoapBinding",type属性指定了这个绑定的类型为"ns1:UserService"。 - <soap:binding>元素用于定义SOAP协议的绑定信息,style属性指定了消息的格式为"document",transport属性指定了传输协议为"http://schemas.xmlsoap.org/soap/http"。 - <wsdl:operation>元素用于定义Web服务的操作,name属性指定了操作的名称为"CXFHelloTanJY"。 - <soap:operation>元素用于定义SOAP协议的操作信息,soapAction属性指定了SOAP操作的命名空间。 - <wsdl:input>元素用于定义Web服务的输入参数,name属性指定了参数的名称为"CXFHelloTanJY",<soap:body>元素用于指定参数的数据格式。 - <wsdl:output>元素用于定义Web服务的输出参数,name属性指定了参数的名称为"CXFHelloTanJYResponse",<soap:body>元素用于指定参数的数据格式。 - <wsdl:service>元素用于定义Web服务的服务信息,name属性指定了服务的名称为"CXFofTanJYService"。 - <wsdl:port>元素用于定义Web服务的端口信息,binding属性指定了端口绑定的名称为"tns:CXFofTanJYServiceSoapBinding",name属性指定了端口的名称为"CXFofTanJYPort",<soap:address>元素用于指定端口的访问地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值