This series of articles on WCF/BizTalk Server 2006 R2 integration will touch upon many aspects of connecting these two critical technologies together in a rich and interesting way. First and foremost, let’s take a look at some of the most common scenarios where BizTalk consumes a WCF service. Before I can dig into topics such as security, binary data processing and transactions, we need to lay a solid foundation with the base use cases.
The article covers the following scenarios:
||Custom Fault Contract|
In this article I will show you how to call WCF services from BizTalk in a variety of ways, handle faults, and trace your output.
Let’s first review the WCF web service that BizTalk Server will call. The data contract used by the service defines a Patient object.
I’ve defined one more data contract which specifies a custom service fault type.
Ok, now that the data definitions have been defined, let’s look at the service contract itself.
I’ve defined three operation contracts in my interface. One, RegisterPatient, accepts and returns a complex type, while also returning a specific fault contract for exceptions. Next I have QueryPatientStatus which accepts a simple type (string) and also returns a simple type. Finally, ModifyPatient accepts a complex type, but returns nothing.
You’ll notice that I have a fourth operation commented out. I used this to test BizTalk behavior with “one-way” services, and saw the behavior mentioned in the documentation (WCF adapters do not support one-way operations). From my WinForm WCF test client this operation works fine (hence I kept it there for testing purposes), but left it commented out for BizTalk usage. If I did NOT leave this commented out, the BizTalk WCF Service Consuming Wizard would not successfully complete. (The BizTalk WCF Service Consuming Wizard does not allow the combination of one-way and request-response operations).
The RegisterPatient operation has a specific fault contract (OperationExampleFault) which you can see utilized here in my operation implementation.
If the new patient is in the “Pacific Northwest” region, I instantiate and throw an OperationExampleFault message. The ModifyPatient operation also throws exceptions, but in that case, I used the more generic “throw new FaultException("Bad patient ID supplied");” command.
The next step was to build a host container (in my case, a self hosted WinForm application) which manages this service lifecycle. My host configuration file identified a behavior which enables service metadata () and used an HTTP endpoint (http://localhost:8880/OperationService).
In order for this next stage to work, I needed to actually start up my WCF service host. Now BizTalk can interrogate this service to extract the necessary metadata. Within Visual Studio.NET, I chose “Consume WCF Service” from the “Add Generated Items” project menu. Note that this is different than the “Add Service Reference” project option available when referencing WCF services in other .NET project types.
I could generate the metadata files explicitly via the WCF svcutil.exe function (e.g. svcutil /t:metadata http://localhost:8880/OperationService?WSDL) , but instead, I chose the first option, which more closely resembles the Add Web Reference walkthrough you’re probably familiar with. If the service host is up and running and a valid URI is entered, then the corresponding WSDL is returned.
Upon completion of the wizard, a series of files are added to the BizTalk project. (Note: you don’t HAVE to use this wizard if you don’t like all the files generated, but for simplicity sake, let’s stick with it). For me these files include:
- PatientService_tempuri_org.xsd – Contains all the operation contracts (e.g. RegisterPatient, RegisterPatientResponse)
- PatientService_act_org_2004_07_Seroter_BizTalkWCFTutorials_OperationExample.xsd – Holds the contract definition of both “Patient” and the custom fault.
- PatientService_schemas_microsoft_com_2003_10_Serialization.xsd – Contains all the base XSD types (I exclude this file from my solution, to no ill effect).
- PatientService.odx – Holds the multi-part message types and port types for calling the WCF service.
- PatientService.BindingInfo.xml – A binding file for the send port which uses the transport (e.g. TCP, WsHTTP, etc) identified.
- PatientService_Custom.BindingInfo.xml – Another binding file for the “WCF-custom” adapter which surfaces many more WCF properties you may want to use.
My next step was to actually create an orchestration that called the WCF service. I didn’t use the auto-generated PatientService.odx orchestration because I didn’t want to be forced to redo work if I regenerated my solution bits later on. So, I created another orchestration which simply uses the types identified in the auto-generated orchestration.
The first thing to do in my new orchestration was to create messages. Unlike with the SOAP adapter, I didn’t point to “web messages.” Instead, I point to the multi-part messages created by the wizard.
Note that EVERY WCF parameter, whether complex OR simple type is represented as a schema type. Unlike with the SOAP adapter and simple type parameters, the WCF simple type messages must be created the same ways that a complex message is created (e.g. map). For the SOAP adapter, the simple types did not show up in the generated schema, but as you can see here, there is a complex type created for my simple string input parameter.
Notice that you can, however, distinguish any of these fields to access input/output values. Above, you can see that I distinguished the QueryPatientStatusResult element (of type string) so that I could quickly read the response value.
My orchestration is set up to call each of my three WCF services in sequence.
In each case, I used a BizTalk map to instantiate the “request” message for the service. And for each scenario, I published a trace message upon successful completion of the call. Note that even though ModifyPatient returns “void”, there is still a BizTalk message that is associated with it. This message has no content, but you have to specify the message variable and connect it to the port nonetheless. For the WCF service calls that DO return a result, I used a distinguished field to yank that value and throw it into my trace log.
Deploy and Test
The project was now ready to build and deploy. Upon successful deployment, I created a receive port/location to absorb the “starter” message which triggers the orchestration. For the send port, I chose to “import … bindings” and pointed to each of the binding files generated by the BizTalk WCF Service Consuming Wizard.
Notice that I have two ports (corresponding to the two binding files that get auto-generated by the BizTalk wizard). One uses the WCF-WSHttp adapter, and the other uses the WCF-Custom adapter. The WCF-Custom adapter is using the WSHttp binding, but, the adapter surfaces more configuration settings than the plain WCF-WSHttp adapter.
The service endpoint is displayed on the “General” tab of the WCF-WSHttp adapter configuration.
Notice that we have three “actions” identified because all three WCF service calls from the orchestration use this same port. Based on the operation name in the orchestration, the appropriate “action” is used.
After first binding my orchestration to this generated WCF-WSHttp port, and starting the application, I dropped my trigger message into the folder polled by the receive location. I could monitor my progress by using the DebugView tool (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx). I should see statements printed by both the WCF service, and the orchestration itself if a successful response is received.
So I could see success all around. Now let’s say that I wished to actually watch the payloads travel across the wire. With the WCF-WSHttp adapter configuration I can’t enable that behavior. However, if I used the auto-generated WCF-Custom send port, I have much more control of my WCF client (i.e. BizTalk Server). Specifically, I want my good friend TCPTrace (http://www.pocketsoap.com/tcptrace/) to watch the raw XML travel back and forth. However, I can’t simply switch my URL to point to the TCPTrace listener as I’ll end up with service addressing errors. However, I CAN use the WCF “clientVia” behavior configuration to enable the service to properly accept forwarded traffic. On the “Behaviors” tab of the WCF-Custom adapter configuration, I chose to add an extension, and selected “clientVia.”
In the “viaUrl” property of that behavior extension I set the Url to be http://localhost:8800/OperationService. This is a different port (8880 vs. 8800) than my actual service endpoint is listening on. Once I start up TCPTrace, I chose to listen on port 8800 and forward traffic to 8880.
After switching my orchestration binding to use this other send port, and dropping my “trigger” message, I can now see the raw XML transmission.
This is a great way to debug or observe the pure XML object before it gets serialized to an object.
So what about that fancy Fault Contract I created at the beginning? Where does that come into play? Because my WCF service operation was decorated with the FaultContract attribute, the fault schema was generated, and my port for the RegisterPatient operation has an available fault message.
How do I use this? My call to RegisterPatient was wrapped in a non-transactional scope with an exception handler defined. If I looked at the available “Exception Object Types”, I could see that there’s a new exception type matching my custom fault.
Neat. So, I simply choose that exception type, and, assuming an “Exception Object Name” of faultEx, the following code in my exception handling block works perfectly:
//ErrorMessage is the name of the property in my custom fault contract
System.Diagnostics.Trace.WriteLine("Orchestration failed calling two way (complex) service. Error: " + faultEx.detail.ErrorMessage);
Almost done. After redeploying this solution, I had to change my send port. Why? Because right now, my send port “Messages” tab is configured to take the WCF service result message, and yank out the “body” from the SOAP envelope.
Why is this bad? Because our fault message is based on a root node of OperationExampleFault and this bit of XML is buried a few layers down within the fault message returned by the service. If we leave this “Body” setting on the port, we’ll get an orchestration “mismatch” error because we’re trying to serialize the whole fault message into the expected OperationExampleFault type.
How do we handle this situation? By using the “Body path expression” feature of the port! Now, you may ask yourself (as I did), why do I want to specify an XPath targeting the fault message, when most of the time a fault is NOT being returned by the service? What happens when I get my regular, “good” result? The key is how you write the Xpath expression. By utilizing a Xpath “union” command (the “|” token), you can include all possible result values, and whichever matches the service response gets used. In my case, the “Body path expression” is:
/*[local-name()='Fault']/*[local-name()='Detail']/* | /*[local-name()='RegisterPatientResponse'] | /*[local-name()='QueryPatientStatusResponse'] | /*[local-name()='ModifyPatientResponse']
Here I have a union of node sets, and per the documentation, the first match is used. So, if I have a fault, then the OperationExampleFault node will be pulled from the /Fault/Detail path, and if there is no fault, the appropriate successful response value is returned to the orchestration. This can be a tricky concept, so I encourage you to play around with this.
What happens when I triggered an exception? Via TCPTrace I could see the raw fault XML.
Notice the OperationExampleFault node nested within the Fault container. If I reviewed the DebugView, I saw that the orchestration caught the custom fault, and printed out the value stored in the ErrorMessage property.
Performing "Messaging Only" WCF Service Call
The final “calling WCF services” scenario centers on calling a service without using an orchestration. This was a feature of the SOAP adapter introduced in BizTalk Server 2006, and still remains with the WCF adapter.
Let’s assume that I have walked through the BizTalk WCF Service Consuming Wizard and have all my schemas. One benefit of having simple type parameter still require a BizTalk schema type is that you can now call simple type parameter services without an orchestration. Using the SOAP adapter, you have no easy way of instantiating simple types (without a custom pipeline or orchestration) and can only easily call services with complex (schema) types. In my case, I want to call my ModifyPatient operation (which accepts a string value and returns void) using only the BizTalk messaging layer. How do I do this? Easily. I first created a new receive port/location to absorb my trigger message. Then, I created a static one-way send port. I have nothing waiting for a response message, so I don’t want to create a solicit-response port. My new send port uses the same endpoint Url (http://localhost:8880/OperationService) as my auto-generated port(s). After adding a send port filter subscribing to messages from my new receive port, I added the final, crucial step. I have to add an outbound map to take my trigger message and create the ModifyPatient request message for the adapter. Once all that is in place, I could drop a file into my directory polled by my new receive location, and watch my service get called.
Sure enough, my service was called successfully from the BizTalk messaging layer. What if I trigger an exception now? Even though this is a “one way” service via the “void” response type, this service still behaves in a synchronous fashion. We get a passive acknowledgement that the service completed with no errors, hence why we have an empty response message auto-generated for this service. So, any fault raised in this pseudo-one-way service gets bubbled up. In this case, the exception is returned and we get a suspended message inside BizTalk Server.
So what have we seen here? I’ve shown you how to call WCF services in a variety of ways (complex vs. simple types, one-way vs. request-response, orchestration vs. messaging) and use the wizard-generated components to significantly speed up development. We also looked at some tricks to enable deeper tracing via TCPTrace, as well as supporting custom Fault contracts inside an orchestration.
My next article will explain each of the WCF security configurations that are supported from BizTalk Server.
Once this entire BizTalk+WCF series is complete, I will make the entire source code available.
Questions, comments or corrections? Go ahead and leave a comment on my blog post about this article.
You can read more about BizTalk, SOA and enterprise architecture on my blog at http://seroter.wordpress.com.