Why does WCF wrap request/response types in another XML element, and how to prevent this?

10,732

Solution 1

I eventually switched over to use Message Contracts using a TypedMessageConverter which I was introduced to via this question's answer. That was the missing piece here.

Solution 2

IIRC, WCF by default uses the 'Wrapped' message style. If you want to be able to control how messages are serialized, you can define explicit messages by decorating with the MessageContractAttribute. With explicit message contracts, you can set the IsWrapped property to false.

In your case I think that EchoRequest and EchoResponse shouldn't be DataContracts at all, but rather MessageContracts. They look a lot like MessageContracts to me.

Share:
10,732
codekaizen
Author by

codekaizen

I create bugs.

Updated on June 04, 2022

Comments

  • codekaizen
    codekaizen about 2 years

    I have a simple echo service where I've defined one operation method and a pair of types for request/response:

    [ServiceContract(Name = "EchoService", 
                     Namespace = "http://example.com/services", 
                     SessionMode = SessionMode.NotAllowed)]
    public interface IEchoService
    {
        [OperationContract(IsOneWay = false,
                           Action = "http://example.com/services/EchoService/Echo", 
                           ReplyAction = "http://example.com/services/EchoService/EchoResponse")]
        EchoResponse Echo(EchoRequest value);
    }
    

    The data types:

    [Serializable]
    [DataContract(Namespace = "http://example.com/services/EchoService", 
                  Name = "EchoRequest")]
    public class EchoRequest
    {
        public EchoRequest() { }
    
        public EchoRequest(String value)
        {
            Value = value;
        }
    
        [DataMember]
        public String Value { get; set; }
    }
    
    [Serializable]
    [DataContract(Namespace = "http://example.com/services/EchoService", 
                  Name = "EchoResponse")]
    public class EchoResponse
    {
        public EchoResponse() { }
    
        public EchoResponse(String value)
        {
            Value = value;
        }
    
        [DataMember]
        public String Value { get; set; }
    }
    

    Invoking Message.CreateMessage() on an instance of EchoRequest yields:

      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
        <s:Header />
        <s:Body>
          <EchoRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/services/EchoService">
            <Value>hello, world!</Value>
          </EchoRequest>
        </s:Body>
      </s:Envelope>
    

    ...which is exactly what I want. However, it appears that the service is expecting the message body to be further wrapped in another XML element, like this:

      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
        <s:Header />
        <s:Body>
          <Echo xmlns="http://example.com/services">
            <EchoRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/services/EchoService">
              <Value>hello, world!</Value>
            </EchoRequest>
          </Echo>
        </s:Body>
      </s:Envelope>
    

    UPDATE: Thanks to Mark's reply, I've explored MessageContract instead of DataContract on the request/response types. This seems closer to what I want, but now it's going too far, and not expecting the outer type element, "EchoRequest".

    This is confusing since somehow Message.CreateMessage appears to unfailingly produce the correct XML, so it's apparently using some default serialization which I'd like to configure the service to accept. Am I just misunderstanding how Message.CreateMessage works?

  • codekaizen
    codekaizen almost 15 years
    Thanks Mark, your suggestion lead me down a path which helped map my understanding of SOAP to WCF more closely. However, things are still not working quite right, and I edited the question, if you're game to follow up.