Using WCF MessageContract to Model A Composite Schema

I have been working on a project where I needed to take an XML Schema
Definition created in BizTalk and create a WCF service that would
receive messages using the same schema. In this particular case, the XSD
was a composite schema and was too complex for svcutil to automatically
generate a data contract. (I prefer to create data contracts by hand
anyway. The code generated by svcutil is a mess.) Attempting to model
this using just the DataContract and DataMember attributes
proved to be impossible because you can’t specify an XML namespace on a
DataMember.

For example, if you have an XSD that imports elements from another XSD
with a different target namespace, when you try and model it as a data
contract there is no way to specify the that the imported schema
elements belong to a different namespace. So if you have an XML document
like this:

Sample XML message

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8" ?>
<ns0:MyMessage xmlns:ns0="http://someservice.example.com/">
<ns0:Message>
<ns0:MyElement>Data</ns0:MyElement>
</ns0:Message>
<ns1:OtherMessage xmlns:ns1="http://someservice.example.com/other/">
<ns1:MyOtherElement>OtherData</ns1:MyOtherElement>
</ns1:OtherMessage>
</ns0:MyMessage>

You might try and model that schema with something that looks like this:

DataContract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[DataContract(Namespace = @"http://someservice.example.com/")]
public class MyMessage
{
[DataMember(Order = 1)]
public Message Message { get; set; }

[DataMember(Order = 2)]
public OtherMessage OtherMessage { get; set; }
}

[DataContract(Namespace = @"http://someservice.example.com/")]
public class Message
{
[DataMember(Order = 1)]
public string MyElement { get; set; }
}

[DataContract(Namespace = @"http://someservice.example.com/other/")]
public class OtherMessage
{
[DataMember(Order = 1)]
public string MyOtherElement { get; set; }
}

If you use DataContract and DataMember attributes exclusively WCF will
produce XML that looks like this:

WCF DataContract XML

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8" ?>
<ns0:MyMessage xmlns:ns0="http://someservice.example.com/">
<ns0:Message>
<ns0:MyElement>Data</ns0:MyElement>
</ns0:Message>
<ns0:OtherMessage xmlns:ns1="http://someservice.example.com/other/">
<ns1:MyOtherElement>OtherData</ns1:MyOtherElement>
</ns0:OtherMessage>
</ns0:MyMessage>

Note the incorrect namespace prefix on the OtherMessage elements. To get
WCF to model the XSD correctly we need to turn the MessageContract
attribute to get the job done. The MessageContract attribute along with
the MessageBodyMember attribute (among others) gives you full control
over the structure off your XML. So now we take the data contract we
defined earlier for the Message and OtherMessage elements and combine it
with a message contract to get the final result we are looking for.

MessageContract

1
2
3
4
5
6
7
8
9
[MessageContract(WrapperNamespace = @"http://someservice.example.com/")]
public class MyMessage
{
[MessageBodyMember(Namespace = @"http://someservice.example.com/")]
public Message Message { get; set; }

[MessageBodyMember(Namespace = @"http://someservice.example.com/other/")]
public OtherMessage { get; set; }
}