为什么WCF中的XmlRoot属性被忽略,以及如何解决这个问题

我们已经观察到,当我们公开使用具有各种xml序列化属性修饰的类的WCF服务时,尽管我们在接口上使用了XmlSerializerFormat属性,但任何操作参数的任何XmlRoot属性都会被完全忽略。 参数的名称空间始终是服务的名称空间,而不是我们指定的名称空间。

这引起了我们的问题,因为它似乎不与ASMX向后兼容,也因为我们正在使用BizTalk,并且需要对交换的XML的形状进行更严格的控制。

然后几个问题 -

  • 任何人都知道这个决定背后的基本原理是什么?
  • 任何人都知道这是怎么发生的? 我的印象是WCF和XmlSerializerFormat属性使用XmlSerialiser对这些类型进行序列化,这意味着XmlRoot应该被考虑进去,怎么会出现这种情况呢? (这是否仅仅是因为考虑到SOAP信封,参数不是根?)
  • 最重要的是 - 任何人都知道是否有办法“强制这个问题” - 即让参数成为我们选择的命名空间的参数?
  • 我看过这篇文章,但我不相信这与我的问题有关 -

    根据Wagner Silveira的要求 - 我曾经测试过的合同是 -

    [ServiceContract(Namespace = "http://servicecontract"),
     XmlSerializerFormat(Style = OperationFormatStyle.Document)]
    public interface ITestService
    {
        [OperationContract]
        MyOtherType MyTestMethod(MyType obj);
    }
    
    // Composite class for DCS and XMLS
    [Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")] 
    public class MyType
    {
        [XmlAttribute]
        public string StringValue { get; set; }
    }
    
    // Composite class for DCS and XMLS
    [Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")]
    public class MyOtherType
    {
        [XmlAttribute]
        public string OtherStringValue { get; set; }
    }
    

    我假设你使用SOAP作为消息格式。 在这种情况下,你正在序列化的对象不是XML的根,肥皂信封是。 所以这是有道理的,XmlRoot将被忽略。 默认情况下,WCF将为您创建一个消息协定并命名响应,并且它具有该服务的命名空间。 你可以做的就是创建你自己的消息协定来完全控制SOAP。

    创建以下两个类:

    [MessageContract]
    public class MyTestMethodRequest
    {
        [MessageBodyMember( Namespace = "http://datacontract" )]
        public MyType MyType;
    }
    
    [MessageContract]
    public class MyTestMethodResponse
    {
        [MessageBodyMember( Namespace = "http://datacontract" )]
        public MyOtherType MyOtherType;
    }
    

    然后将您的服务操作的签名更改为以下内容。

    [OperationContract]
    public MyTestMethodResponse MyTestMethod( MyTestMethodRequest request )
    {
        return new MyTestMethodResponse {
            MyOtherType = new MyOtherType {
                OtherStringValue = "bar"
            }
        };
    }
    

    现在,如果您以SOAP消息为例,您应该看到以下内容:

    请求

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Header>
        <Action xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"
                s:mustUnderstand="1">http://servicecontract/TestService/MyTestMethod</Action>
      </s:Header>
      <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <MyTestMethodRequest xmlns="http://servicecontract">
          <MyType StringValue="foo" xmlns="http://datacontract" />
        </MyTestMethodRequest>
      </s:Body>
    </s:Envelope>
    

    响应

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Header />
      <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <MyTestMethodResponse xmlns="http://servicecontract">
          <MyOtherType OtherStringValue="bar" xmlns="http://datacontract" />
        </MyTestMethodResponse>
      </s:Body>
    </s:Envelope>
    

    我不知道为什么WCF忽略XmlRoot,所以我无法回答你的问题的那部分。 但是我有一些解决问题的方法。

  • 首先从WSDL开始。
    如果您希望将一组特定的XML名称空间应用于发送和接收的消息,请使用WSDL和XML Schema来明确指定它们。

    然后,通过svcutil.exe工具直接从该WSDL生成服务器端存根代码或客户端代理代码。

  • 使用一个自定义的ServiceHost
    在这个链接中描述的另一个选项是使用一个自定义的ServiceHost来覆盖WCF的决定,忽略消息类型上的XmlRoot或XmlType属性。


  • 如果您选择采用WSDL-First方法,那么WSDL应该如下所示:

    <?xml version="1.0" encoding="utf-8" ?>
    
    <definitions
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        targetNamespace="urn:The-Service-namespace"
        xmlns:tns="urn:The-Service-namespace"
        xmlns:s="http://www.w3.org/2001/XMLSchema"
        xmlns:n0="urn:The-Request-namespace"
        xmlns:n1="urn:The-Response-namespace"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        elementFormDefault= "unqualified"
      >
    
        <types>
          <s:schema targetNamespace="urn:The-Request-namespace" >
            <s:complexType name="Type1">
              <s:sequence>
                <s:element name="x" minOccurs="1" maxOccurs="1" type="s:string"/>
              </s:sequence>
            </s:complexType>
            <s:element name="Type1" type="n0:Type1" />
          </s:schema>
    
    
          <s:schema targetNamespace="urn:The-Response-namespace" >
            <s:complexType name="Type2">
              <s:sequence>
                <s:element name="x" minOccurs="1" maxOccurs="1" nillable="false" type="s:string"/>
                <s:element name="y" minOccurs="1" maxOccurs="1" nillable="false" type="s:int"/>
                <s:element name="z" minOccurs="1" maxOccurs="1" nillable="false" type="s:boolean" />
              </s:sequence>
            </s:complexType>
            <s:element name="Type2" type="n1:Type2" />
          </s:schema>
    
        </types>
    
    
    
    <message name="RequestMessage">
       <part name="inPart1" element="n0:Type1" />
    </message>
    <message name="ResponseMessage">
       <part name="outPart1" element="n1:Type2" />
    </message>
    
    
    
    <portType name="PortTypeName">
      <operation name="Method1">
          <input message="tns:RequestMessage" />
          <output message="tns:ResponseMessage" />
       </operation>
    </portType>
    
    
    
    <binding name="InterfaceName" type="tns:PortTypeName">
        <soap:binding
           transport="http://schemas.xmlsoap.org/soap/http"
           style="rpc" />
    
        <operation name="Method1">
            <soap:operation soapAction="" style="document" />
            <input>  <soap:body use="literal" /> </input>
            <output> <soap:body use="literal" /> </output>
        </operation>
    </binding>
    
    </definitions>
    

    这个WSDL非常简单 - 它定义了一个单一的操作,带有单个请求消息和单个响应消息。

    注意有三个xml命名空间:

  • 金塔:该-服务的命名空间
    用于包装请求和响应的元素 - <SOAP:body>中的第一个元素
  • 金塔:该请求的命名空间
    用于包装在请求包装器中的元素,该元素被反序列化为Type1的实例。
  • 金塔:该-响应命名空间
    用于封装在响应包装器中的元素,该元素被反序列化为Type2的实例。
  • 如果您的Web服务接口更复杂,拥有更多的操作并因此提供更多的请求和响应消息类型,您可以为所有这些附加类型添加更多名称空间(如果您愿意)。

    链接地址: http://www.djcxy.com/p/34293.html

    上一篇: Why does the XmlRoot attribute gets ignored in WCF and how to overcome this

    下一篇: Group by in LINQ with 2 attribtes