During a recent mission at a new client, I was tasked to help migrate BizTalk 2006 R2 applications and solutions to BizTalk Server 2009. Part of this migration involves converting currently exposed SOAP web services to compatible WCF services.
While the BizTalk services are likely to be exposed as SOAP web services for a long time, WCF is definitely the way to go, as the transition to WCF will enable more flexible scenarios in the long run, such as using named pipe messaging between services on the same machine for instance.
In this post, part of a three-part series, I will describe the issues one faces when trying to make the transition smooth and as transparent as possible to existing service customers, as well as outline the steps to be taken in order to achieve the migration.
Exposing SOAP-compatible BizTalk WCF Services
Using the WCF Services Publishing Wizard
The first step to a successful migration is actually to publish a WCF service! Using the WCF Services Publishing Wizard, available in the BizTalk installation folder or from a custom tool in Visual Studio, one can have an IIS-hosted service up and running in no time.
You’ll notice that there are not as many options available to customize the generated service, as compared to the BizTalk Web Services Publishing Wizard. Instead, the most prominent choice in the wizard is probably the selection of a WCF binding.
As you know, the most appropriate choice for a SOAP-compatible service is to select the WCF-BasicHttp adapter that wraps the basicHttpBinding WCF binding.
Identifying the differences between SOAP-based and WCF-based web services
Looking at the WSDL from both services, the first observation is that SOAP-based services expose two endpoints (for both Soap 1.1 and Soap 1.2 requests) whereas WCF-based services only expose a single endpoint.
This is a consequence of WCF-BasicHttpBinding only supporting Soap 1.1 requests.
When using the BizTalk Web Services Publishing Wizard one has the options to customize various aspects of the exposed service. By default, however, services are exposed using the Document/Literal wrapped WSDL style.
This means a SOAP request from a client looks like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inp="http://services.sample.org/BizTalk/2010/soap/input" xmlns:inb="http://Inbound.Input"> <soapenv:Header/> <soapenv:Body> <inp:WebMethod> <inb:Root> <Record Id="?"/> </inb:Root> </inp:WebMethod> </soapenv:Body> </soapenv:Envelope>
In contrast, BizTalk WCF services that use the basicHttpBinding are configured to use the Document/Literal WSDL style, where the name of the web method does not encapsulate the body of the request.
Thus, a WCF request from a client looks like so:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inb="http://Inbound.Input"> <soapenv:Header/> <soapenv:Body> <inb:Root> <Record Id="?"/> </inb:Root> </soapenv:Body> </soapenv:Envelope>
The same differences apply to the service responses, where, in the case of a SOAP-based service, the body of the response is wrapped in an additional XML tag.
Clearly, there are several adaptations that need to be made to make our newly WCF service compatible with what existing clients expect.
Customizing the service description
In order to have the WCF service conform to what existing clients expect, I think the simplest solution is to prevent WCF from synthesizing a WSDL on demand and instead serve a custom WSDL based off a file.
So I just copied the existing WSDL from the SOAP-based web service and altered the addresses from the Soap and Soap12 endpoints like so:
<wsdl:service name="WebService"> <wsdl:port name="WebServiceSoap" binding="tns:WebServiceSoap"> <soap:address location="http://localhost/SampleWcf/WebService.svc" /> </wsdl:port> <wsdl:port name="WebServiceSoap12" binding="tns:WebServiceSoap12"> <soap12:address location="http://localhost/SampleWcf/WebService.svc" /> </wsdl:port> </wsdl:service>
I then placed this file in the directory tree of the published WCF service. For instance, at the root of the service folder.
In order to specify a custom WSDL for our WCF service, we need to modify the service configuration file to point to our hand-crafted WSDL file. This is achieved by adding an externalMetadataLocation attribute to the service behavior.
<serviceBehaviors> <behavior name="ServiceBehaviorConfiguration"> <serviceDebug httpHelpPageEnabled="true" httpsHelpPageEnabled="false" includeExceptionDetailInFaults="false" /> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" externalMetadataLocation="/../ServiceDescription.wsdl" /> <!-- add this attribute --> </behavior> </serviceBehaviors>
With these changes, the WCF service now exposes our custom WSDL.
Customizing the BizTalk receive location
As I’ve described earlier, SOAP-requests carried out by existing clients will have an additional layer of XML tags that will make the existing BizTalk implementation choke. Likewise, the default response produced by WCF will lack this additional layer expected by the calling clients.
The BizTalk WCF adapters support extracting part of the incoming request based on a forward-only XPath expression. This makes it very straightforward to convert the incoming SOAP message to what is expected downstream.
This is achieved by specifying the « Inbound BizTalk message body » path property in the adapter configuration Messages tab:
When processing the response, the BizTalk WCF adapters support customizing the document that will be sent over the wire. This is achieved by specifying a template of the document we want to send out.
In the adapter configuration Messages tab, modify the « Outbound WCF message body template » property like so:
With these simple changes, existing clients can continue to use the BizTalk services over SOAP with the minimum impact.
Hopefully, the only change required on the client-side would be the configuration of the service endpoint address.
This post has shown how to achieve a nearly seemless migration of existing SOAP-based BizTalk services to WCF services without impacting existing customers. It turns out that the migration did not require a single change on the implementation side.
That’s what I like about WCF : it’s all configuration!
If you’ve been following closely, however, you must have noticed an unfortunate side effect of this migration : Soap 1.2 request do not work! In the next post, we’ll see how to handle both Soap 1.1 and Soap 1.2 compatibility with WCF.
The last post in the series will talk briefly about handling SOAP-headers with WCF services.