2010-01-06 13 views
8

Hemos observado que cuando exponemos un servicio WCF que utiliza clases decoradas con varios atributos de serialización xml, a pesar del hecho de que utilizamos el atributo XmlSerializerFormat en la interfaz cualquier atributo XmlRoot en cualquiera de los parámetros de la operación se ignora por completo. El espacio de nombre de los parámetros es siempre el del servicio y no el que especificamos.¿Por qué se ignora el atributo XmlRoot en WCF y cómo superarlo?

Esto nos causa problemas, ya que no parece ser compatible con ASMX y también porque estamos utilizando BizTalk, y necesitamos tener un control más estricto sobre la forma de los XML intercambiados.

Algunas preguntas a continuación -

  1. Alguien sabe cuál es el fundamento detrás de esta decisión?
  2. ¿Alguien sabe cómo está sucediendo esto? Estaba bajo las impresiones que WCF, con el atributo XmlSerializerFormat, utiliza el XmlSerialiser para serializar los tipos , lo que sugeriría XmlRoot deben tenerse en cuenta, cómo vienen este no es el caso? (¿Es sólo por el hecho de que, teniendo el sobre SOAP en cuenta, el parámetro no es root?)
  3. más importante - nadie sabe si hay una manera de 'forzar la situación' - es decir, obtener los parámetros para ser del espacio de nombre de nuestra elección?

que he visto this puesto, pero no creo que es relevante a mi pregunta -

Según la petición de Wagner Silveira - los contratos que utiliza para probar esto son -

[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; } 
} 

Respuesta

2

Supongo que está utilizando SOAP como formato de mensaje. En este caso, el objeto que está serializando no es la raíz del XML, el sobre del jabón es. Por lo tanto, tiene sentido que XmlRoot sea ignorado. Por defecto, WCF creará un contrato de mensajes para usted y le dará un nombre a la respuesta y tendrá el espacio de nombres del servicio. Lo que puede hacer es create your own message contract para tener control total sobre SOAP.

crear las dos clases siguientes:

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

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

a continuación, cambiar la firma de su operación de servicio a la siguiente.

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

Ahora ejemplo si los mensajes SOAP debería ver lo siguiente:

Solicitud

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

Respuesta

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

Gracias John Calculamos la opción MessageContract, parece una gran molestia para algo que acaba de funcionar en ASMX, que es también la razón por la que no estaba seguro sobre el argumento del nodo no raíz, pero supongo que tiene sentido. Personalmente creo que eso es un gran golpe a la compatibilidad con versiones anteriores. –

1

No sé por qué WCF ignora XmlRoot, por lo que no puedo responder esa parte de su pregunta. Pero tengo un par de formas de resolver el problema.

  1. comienzo con WSDL primero.
    Si tiene un conjunto particular de espacios de nombres XML que le gustaría aplicar a los mensajes que se envían y reciben, use WSDL y XML Schema para especificarlos explícitamente.

    A continuación, genere el código de código auxiliar del lado del servidor o el código del proxy del lado del cliente directamente desde ese WSDL a través del the svcutil.exe tool.

  2. use a custom ServiceHost
    La otra opción abierta a usted, que se describe en this link, es utilizar un ServiceHost personalizada que anula la decisión de hacer caso omiso de la WCF XmlRoot o atributos XmlType en tipos de mensajes.


Si se elige el camino para el enfoque WSDL-En primer lugar, el WSDL debe tener este aspecto:

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

Este WSDL es muy simple - se define una sola operación , con un solo mensaje de solicitud y un solo mensaje de respuesta.

cuenta de que hay tres espacios de nombres XML:

  • urn: El Servicio-espacio de nombres
    utilizado para el elemento que envuelve la solicitud y la respuesta - el primer elemento dentro de la < soap: Body >
  • urn: El espacio de nombre de solicitud
    se usa para el elemento envuelto dentro de ese contenedor de solicitudes, que se deserializa en una instancia de Type1.
  • urn: El-Response-espacio de nombres
    utilizado para el elemento de envuelta interior que wrapper respuesta, que consigue deserializar en una instancia de Type2.

Si su interfaz de servicios web es más complicada, tiene más operaciones y consecuentemente más tipos de mensajes de solicitud y respuesta, puede agregar más espacios de nombres, si lo desea, para todos esos tipos adicionales.

+0

Gracias Cheeso. Estoy familiarizado con el enfoque de host personalizado, y de hecho, esto es lo que hemos hecho en un par de lugares, pero esto parece ser una gran pregunta, para una tecnología que lleva la bandera de la interoperabilidad. De todos modos, el propósito de mi pregunta es comprender el comportamiento. Le agradezco que se tome el tiempo para responder. –

Cuestiones relacionadas