In my last MSDN webcast I briefly demonstrated true contract-first development using BizTalk with schemas and orchestrations. I wanted to walk through the steps here, and point out a huge "gotcha" that stumped me for a bit.
So I started constructing this sample app to prove that I could build out and deploy my contract and service before adding any "guts" to the process underneath. I did this by first creating two schemas, representing the messages that go both into and out of my service. At this point, all I have is a deployed BizTalk project with two schemas. Next, I walked through the BizTalk Web Services Publishing Wizard, and chose to Publish schemas as web services.
The value of this capability eluded me for a while when I first started using BizTalk 2004. Why would I ever create a service out of schemas? Most commonly, you'd use this function to create a "fire and forget" asynchronous channel for folks to submit messages with no expectation of response. Then, you could have any series of orchestrations pick up the inbound doc and process it. In my case however, I want to create an actual request-response service. So my next step in the Wizard was to delete the default web service configuration.
I created a new service called TradingPartnerService. And then I added a new request-response web method called SubmitOrder. The parameters for this method were the two schemas I created previously (one for request, one for response).
I then completed the wizard without having it create the pre-configured Receive Location for me. Mainly because I like creating my own. So, into my application I added a new Receive Location (and Receive Port) configured with SOAP and pointing to my fancy new service.
At this point, I have a real-life service with input and output ready to go. Now, obviously if I go and consume this service, BizTalk won't be happy since I've added no subscribers to this inbound message. But, I've still defined my contract and interface prior to any implementation logic.
My next step is to create an orchestration that will consume this service. In my orchestration I created a stock request-response port type and port, and put some fairly basic processing inside. The Messages received and returned are the same types that the service expects.
Then I deployed my orchestration, and bound it to my Receive Port that had a SOAP Receive Location. At this point, everything should work fine. I've got a processing subscriber to take my request message and generate a valid response message to my caller. However, when I called this service from my simple UI, I got a dreaded: System.Web.Services.Protocols.SoapException: internal soap processing failure --> batch completion failure. I hate this error. So freakin' vague. So after checking all sorts of security things (since that's my problem with web services roughly 92% of the time), I kept getting the same error. My suspended message list had a message with no body, but interesting context.
Now what had bugged me up until now was how calling this particular service method (SubmitOrder) actually got the message to my orchestration. Notice in the context above that the calling method is captured. So, I went to the Subscription Viewer in the Administration Console and checked out the subscription for my orchestration. I saw the picture below, and immediately began raining mighty blows upon my head.
Because I had kept the default operation name in the orchestration (Operation_1), that became the service method name it's looking for on inbound messages, NOT the actual name SubmitOrder. So, of course the inbound message couldn't find a subscriber! I went back to my orchestration and changed my port Operation name to match the service method. This is the gotcha.
Once I redeployed my changes, I refreshed my subscription. Now I see a good looking subscription, and calling my web service gives me expected result.
So a couple morals. First of all, you can see how calling a web service actually reconciles to a physical subscription. The method name plays a key part there. On a related note then, that service name needs to match up with a subscriber or you're service call will fail. As you can see, it's completely possible to do contract-first development with BizTalk, and fill in the plumbing logic later on. You just have to make sure you tie all your pieces together!