Distributed Weekly 43
March 26th, 2010 ScottArchitecture
- A Discussion with Allard Buijze on CQRS with the Axon framework
- Windows Server AppFabric Architecture
- UDDI R.I.P.
BizTalk/WF
REST
Architecture
BizTalk/WF
REST
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
<?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
[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
<?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
[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; } }
Since I seem to forget this on a regular basis, I wanted to leave a note to myself for future reference. I also want to post a link to the Tech Talk blog where I found most of this information.
By default, WCF assigns the namespace “http://tempuri.org/” to all web service operations. (For more information on tempuri.org, see the W3C Note on WSDL.) Typically you will want to assign your own namespace to uniquely identify any web services you have deployed. Since WCF has a (seemingly) infinite number of configuration options, there are no fewer than three places you need to set the namespace to keep WCF from using the default tempuri.org.
The first is on the ServiceContract attribute. Set the namespace in the attribute constructor.
[ServiceContract(Namespace = @"http://someservice.example.com/")] public interface ISomeService
The second place it needs to be set is on the ServiceBehavior attribute. Again, set the namespace in the attribute constructor.
[ServiceBehavior(Namespace = @"http://someservice.example.com/")] public class SomeService : ISomeService
The third and final place it needs to be set is the bindingNamespace attribute on the endpoint element in the App.config file.
<endpoint bindingNamespace="http://someservice.example.com/"...> ... </endpoint>
BizTalk/ESB
REST
SQL Server Modeling
WCF
* Courtesy of Sam Gentile
BizTalk / M
REST
SOA
WCF / WF
BizTalk
ESB
REST
SOA
WCF