R – WCF DataContractSerializer doesn’t pick up contract attributes… why not

datacontractdatacontractserializermessagecontractwcf

I have the following type which I use as a message contract in WCF:

[MessageContract(IsWrapped = true, 
                 WrapperNamespace = "http://example.com/services", 
                 WrapperName = "EchoRequest")]
public class EchoRequest
{
    public EchoRequest() { }
    public EchoRequest(String value)
    {
        Value = value;
    }

    [MessageBodyMember(Name = "Value", 
                       Namespace = "http://example.com/services", 
                       Order = 0)]
    public String Value { get; set; }
}

When I generate a proxy to this type using svcutil.exe, I get a client which is able to communicate to a service which hosts it, with the XML namespaces on the elements correct according to the Message Contract attributes.

When I use Message.CreateMessage(...) on an instance of it, the namespaces revert to the default (http://schemas.datacontract.org/2004/07/...). When I use an instance of DataContractSerializer, the same thing happens. I try to pass a namespace to the DataContractSerializer constructor, and only the wrapper gets included in the namespace:

var requestMessage = new EchoRequest("hello, world!");
var serializer = new DataContractSerializer(typeof(EchoRequest), 
                                            "EchoRequest", 
                                            "http://example.com/services");
var stream = new MemoryStream();
serializer.WriteObject(stream, requestMessage);
var data = Encoding.UTF8.GetString(stream.ToArray());

At this, "data" is:

<EchoRequest xmlns="http://example.com/services"
             xmlns:a="http://schemas.datacontract.org/2004/07/TestClient"
             xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <a:Value>hello, world!</a:Value>
</EchoRequest>

Why does the DataContractSerializer appear to ignore the MessageContract attributes? How does svcutil get this work?

Best Answer

It's because message contracts are not data contracts, data contracts use different attributes to mark their classes. Try using a typed message converter;

EchoRequest echoRequest = new EchoRequest{ value = "Hello" };

TypedMessageConverter echoMessageConverter = TypedMessageConverter.Create(
                 typeof(echoRequest),
                 "YourActionNameHere",
                 "http://example.com/services");
Message request = echoMessageConverter.ToMessage(
    echoRequest,MessageVersion.Soap11);

You'll then have a message all ready to go and can pull the request body out if you need to.

Related Topic