2010-01-06 7 views
8

Zauważyliśmy, że gdy udostępniamy usługę WCF, która używa klas ozdobionych różnymi atrybutami serializacji xml, pomimo faktu, że używamy atrybutu XmlSerializerFormat w interfejsie każdy atrybut XmlRoot na którymkolwiek z parametrów operacji zostanie całkowicie zignorowany. Przestrzeń nazw parametrów jest zawsze tą usługą, a nie tym, co określamy.Dlaczego atrybut XmlRoot zostanie zignorowany w WCF i jak przezwyciężyć ten błąd?

Powoduje to problemy, ponieważ nie jest kompatybilny wstecz z ASMX, a także dlatego, że używamy BizTalk i musimy mieć ściślejszą kontrolę nad kształtem wymienianych danych XML.

Kilka pytań następnie -

  1. ktoś wie co jest uzasadnienie za tą decyzją?
  2. Ktoś wie, jak to się dzieje? Byłem pod wrażenia, że ​​WCF, z atrybutem XmlSerializerFormat, wykorzystuje się XmlSerialiser do serialise typy , co sugerowałoby XmlRoot powinny być brane pod uwagę, w jaki sposób przyjść nie jest to przypadek? (Jest to tylko ze względu na fakt, że biorąc kopertę SOAP pod uwagę parametr nie jest root?)
  3. Most ważniejsze - kto wie, czy istnieje sposób, aby „wymusić problem” - tj uzyskać parametry należące do wybranej przez nas przestrzeni nazw ?

Widziałem this post, ale nie wierzę, że jest istotne dla mojego pytania -

Jak na zamówienie Wagner Silveira - kontrakty używałem do testowania tego są -

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

Odpowiedz

2

Zakładam, że używasz SOAP jako formatu wiadomości. W tym przypadku obiekt serializowany nie jest korzeniem XML, koperta mydła jest. Ma to sens, aby XmlRoot był ignorowany. Domyślnie WCF stworzy dla ciebie kontrakt wiadomości i nazwał odpowiedź oraz ma przestrzeń nazw usługi. To, co możesz zrobić, to create your own message contract, aby mieć pełną kontrolę nad SOAP.

Tworzenie dwie następujące klasy:

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

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

Następnie zmień podpis operacji serwisowym poniżej.

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

Teraz, jeśli przykład komunikaty SOAP powinieneś zobaczyć:

Zapytanie

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

Response

<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

Dzięki John Ustaliliśmy opcję MessageContract, wydaje się, że jest dużo kłopotów z czymś, co działało w ASMX, i dlatego też nie byłem pewien co do argumentu węzła root-root, ale myślę, że to ma sens. Osobiście uważam, że to cios dla kompatybilności wstecznej. –

1

Nie wiem, dlaczego WCF ignoruje XmlRoot, więc nie mogę odpowiedzieć na tę część twojego pytania. Ale mam kilka sposobów rozwiązania tego problemu.

  1. Zacznij od WSDL jako pierwszy. Jeśli masz określony zestaw przestrzeni nazw XML, które chcesz zastosować do wiadomości, które są wysyłane i odbierane, użyj WSDL i schematu XML, aby jawnie je określić.

    Następnie wygeneruj kod pośredniczący po stronie serwera lub kod proxy po stronie klienta bezpośrednio z tego pliku WSDL za pośrednictwem the svcutil.exe tool.

  2. use a custom ServiceHost
    Inną opcją dla ciebie otwarte, opisane w this link, jest użycie niestandardowych ServiceHost WCF, który zastępuje decyzję, aby pominąć XmlRoot lub XMLType atrybutów typów komunikatów.


Jeśli zdecydujesz się iść do podejścia WSDL-pierwsze, WSDL powinien wyglądać następująco:

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

Ten WSDL jest bardzo prosta - definiuje pojedynczą operację , z pojedynczym komunikatem żądania i pojedynczym komunikatem odpowiedzi.

Wskazówka istnieją trzy przestrzenie nazw XML:

  • urn: The-Service-namespace
    wykorzystywane do elementu, który owija żądanie i odpowiedź - pierwszy element wewnątrz < SOAP: ciało >
  • urn: Nazwa przestrzeni nazw-Request
    używana do elementu zawiniętego wewnątrz tego owiacza żądania, który zostaje przekształcony w postaci szeregowej w instancję typu 1.
  • urn: obszar nazw-odpowiedzi
    użyty dla elementu zawiniętego wewnątrz tego opakowania odpowiedzi, który zostaje przekształcony w postaci szeregowej w instancję typu 2.

Jeśli twój interfejs usług internetowych jest bardziej skomplikowany, ma więcej operacji, a co za tym idzie, więcej typów wiadomości i odpowiedzi, możesz dodać więcej przestrzeni nazw, jeśli chcesz, dla wszystkich tych dodatkowych typów.

+0

Dzięki Cheeso. Jestem zaznajomiony z niestandardowym podejściem hosta, a tak naprawdę - to jest to, co zrobiliśmy w kilku miejscach, ale wydaje się, że to duże wyzwanie dla technologii, która nosi flagę interoperacyjności. W każdym razie - celem mojego pytania jest zrozumienie zachowania. Doceń, że poświęcasz czas na odpowiedź. –

Powiązane problemy