Exposing a simple BizTalk orchestration as WCF service
One of the requests I recently got (from an existing customer in the recruitment space) was to demo how to expose an orchestration as a WCF service.
Let me wrap this up in a wider context of the SOA (as it will make much more sense) and I'll then reconstruct the series of steps that I actually did to build a simple project from scratch.
A classic case of service orientation is when a business division in an enterprise owns a service (which in reality could well be an entire business process designed & developed as an orchestration that integrates with multiple backend disparate systems) and then we simply expose this as a WCF service so that other areas within the enterprise (or outside depending upon the nature of the service) can consume it for their benefit. For simplicity, lets take a simple business process e.g. 'Credit Check' and go through the process of constructing it and then exposing it as a WCF service. All it takes is an input document (with details of the company, the amount for which its credit-worthiness is being checked and the current date) and after processing this service sends a reply for that company which is a simple boolean - success or failure. As evident, once this business service is built, thoroughly tested & deployed into production, it could easily be consumed by other areas of the enterprise and there should not be any need to rebuild this. So let me go through the walkthrough assuming you just have an elementary knowledge of BizTalk.
Create a new Biztalk project and call it 'SimpleCreditCheck' and setup the strong name in the assembly and call the Application name 'SimpleCreditCheck' as well (in the Deployment Configuration)
Create a new schema and call it Input.xsd (Right click the project -> Add -> Add New Item -> Schema Files). Click on the Root Node and rename it as CompanyDetails
Right click the CompanyDetails node -> Insert Schema Node -> Child Field Element and call it CompID with the default type (xs:string). Repeat this to create CompName with the default type (xs:string), CreditCheckDate with the default type (xs:string) and Amount with type as (xs:int)
So now your CompanyDetails root node has four elements under it: CompID, CompName, CreditCheckDate, Amount
Create a new schema and call it Output.xsd (Right click the project -> Add -> Add New Item -> Schema Files). Click on the Root Node and rename it as CreditCheckResult
Right click the CreditCheckResult node -> Insert Schema Node -> Child Field Element and call it CompID with the default type (xs:string). Repeat this to create CreditCheckDate with the default type (xs:string) and ApprovedStatus with type as (xs:boolean)
So now your CreditCheckResult root node has three elements under it: CompID, CreditCheckDate, ApprovedStatus
Create a new Orchestration and call it CreditCheck.odx (Right click the project -> Add -> Add New Item -> Orchestration Files).
Drop a Port from the toolbox and call it RP_CreditCheck click Next and type RPT_CreditCheck for the port type name (make sure you choose Communication pattern as request-response and Access Restriction as public). Click Next and choose the defaults and click Finish
Drop a Receive, Transform, Call Rules, Send shapes from the tool box.
In the Orchestration View, Right click on the Message -> New Message -> Call it MsgIn and choose the type as CreditCheck.Input
Again In the Orchestration View, Right click on the Message -> New Message -> Call it MsgOut and choose the type as CreditCheck.Output
Click on the Receive shape and select the Message as MsgIn Activate as True and Operation as RP_CreditCheck.Operation_1.Request in the Properties View
Click on the Construct Message part of the Transformer and in Properties window select Message Constructed as MsgOut.
Double click the transformer and select the MsgIn as Source and MsgOut as Destination and click OK.Join up the CompID and CreditCheckDate fields on the input & output schemas and select the Value = True (property in the transformer for ApprovedStatus)
Click on Save All and close the transformer pane.
Click on the Send shape and select Message as MsgOut and operation as RP_CreditCheck.Operation_1.Response in the properties.
Fire up the Business Rules Composer -> Right click Policy -> Add New Policy -> call it CreditCheckPolicy. Right click on version 1.0 -> New Rules -> Call it ApproveCredit. Right click on the IF (right pane) -> Conditions -> Predicates -> Less than equal
On the Fact Explorer click on the XML schema -> Schema -> Browse -> choose both Input.xsd & Output.xsd using the shift keys and click open
Drag the Amount field from the Input.xsd and drop it on the IF argument1. Place a value e.g. 1000 as argument2. Drag the ApprovedStatus from the Output schema into the THEN pane under the Actions. Select True from the drop down. The entire logic would then read if the Input amount is <= 1000, ApprovedStatus = True
Do the same steps for disapproval. Under the CreditCheckPolicy. Right click on version 1.0 -> New Rules -> Call it DenyCredit. Right click on the IF (right pane) -> Conditions -> Predicates -> Greater than. Drag the Amount field from the Input.xsd and drop it on the IF argument1. Place a value e.g. 1000 as argument2. Drag the ApprovedStatus from the Output schema into the THEN pane under the Actions. Select False from the drop down. The entire logic would then read if the Input amount is > 1000, ApprovedStatus = False
Now that you have the Policy & Rules in Place -> Right Click version 1.0 and select Save then Publish & finally Deploy. Close the Business Rules Composer.
Go back to the Orchestration and double click on the call Rules shape - select the CreditCheckPolicy and Input & Output schemas as parameters.
Right click the project and click on Deploy. Once the project has deployed successfully, you can now choose to publish it as a WCF service.
Go to the Tools in the Visual Studio (alternatively from Start -> All Programs -> BizTalk Server 2006 as well) and select the BizTalk WCF Service Publishing Wizard -> Next -> Select the Service endpoint option, (this means that you will publish a WCF service from an orchestration in an assembly) and Select WCF-BasicHttpfrom the Adapter name (Transport type) drop-down list.
Select the Enable metadata endpoint check box to make the WCF receive location hosted by IIS publish its WCF service metadata. (this sets the httpGetEnabled attribute of the to true in Web.Config. This metadata can thus be retrieved when an HTTP/GET request calls it - useful for generating proxy to build a client to test this service)
Select the Create BizTalk receive locations in the following application option to create the receive ports and locations corresponding to each generated .svc file for the WCF-BasicHttp adapter. Choose SimpleCreditCheck Application. Click Next
On the Create WCF Service page, select Publish BizTalk orchestrations as WCF service, and then click Next.
In the BizTalk assembly file (*.dll) choose the default SimpleCreditCheck.dll and click Next
Choose the defaults for the BizTalk assembly descriptions and click Next
Select the default URI, "https://tempuri.org/" in the Target namespace of the WCF service (unless you want your published WCF service to use a different URI in which case you can specify it here) and click Next
Leave the default location https://localhost/SimpleCreditCheck in the Location.
and Select the Allow anonymous access to WCF service option, and then click Next.
On the WCF Service Summary page, click Create to create the service and on the Completing BizTalk WCF Service Publishing Wizard page, click Finish
Click on the Start -> Administrative tools -> IIS and right click the SimpleCreditCheck on the Deafult Web Site and make sure an appropriate Application Pool is selected or create a new Application Pool. Also, make sure that on the ASP.NET pane, ASP.NET framework version 2.0.* (and not 1.1.*) is selected. Also copy over (on a Notepad) the full WSDL address from the .svc file that the wizard generated for building the proxy classes for Client.
Go back to the BizTalk admin console and check - the wizard would have created the Receive Port & Receive Location for you - configure them so that they are bound to the Logical Receive Port and start the application
To create a client for this WCF serivce, go to the Visual Studio and create a new project (Visual C# -> Windows -> Windows Application) lets call it SimpleCreditCheckClient
Go to the Start -> All Programs -> Microsoft Windows SDK -> CMD Shell and change the directory to point towards your SimpleCreditCheckClient project directory created above. Use the svcutil.exe command saved in the steps above and paste it on the command window
Click Enter to create the proxy class & config file
Right Click SimpleCreditCheckClient project -> Add -> Add Existing Item -> click on BizTalkServiceInstance.cs and output.config files, and then click Add (rename output.config to App.config). Also make sure System.ServiceModel.dll is referenced (Right Click References -> Add Reference -> Choose System.ServiceModel from the .NET component)
On the Form, drag & drop 5 textboxes for CompID, CompName, CurrentDate, Amount & Status fields. Label them by dragging & dropping 5 labels of the same name. Disable the Status textBox. Drag & Drop a button and call it Check. Double click the button 'Check' and have the following code:
SimpleCreditCheck_CreditCheck_RP_CreditCheckClient proxy = new SimpleCreditCheck_CreditCheck_RP_CreditCheckClient();
CompanyDetails comp = new CompanyDetails(); CreditCheckResult result = new CreditCheckResult ();
comp.CompID = tbCompID.Text; comp.CompName = tbCompName.Text; comp.CreditCheckDate= tbCreditCheckDate.Text; comp.Amount= Convert.ToInt32(tbAmount.Text);
result = proxy.Operation_1(comp);
if (result.ApprovedStatus) tbStatus = "Passed"; else tbStatus = "Failed";
Run the CreditCheckClient - Enter the Amount and check - change the Rules to a new version - deploy & test. Happy to help out if you have any issues. Let me know if you've had to do similar demos/PoCs and would like to share your thoughts.
Comments
Anonymous
November 26, 2008
Good article. Following your steps, I could create solution on first attempt.Anonymous
January 14, 2009
How does CompanyDetails type and CreditCheckResult type in your WCF client maps to schemas you have created in your Biztalk project? When you call result = proxy.Operation_1(comp); does WCF client serializes comp and result object to validate schema inside biztalk?Anonymous
January 15, 2009
Yes, WCF behind the scene does the serialization for you (when you invoke the svcutil.exe to generate the proxy classes, it will automatically generate the corresponding serializable using the default DataContractSerializer).Anonymous
January 15, 2009
Good article.Following your step, I am getting following error: Error in deserializing body of reply message for operation 'Operation_1'Anonymous
January 16, 2009
Hi, I have got one .net class called StatusMessage. I have two console application, one publisher and other subscriber. Ideally I want to transmit StatusMessage type message from publisher to subscriber using BizTalk orchestration. subscriber app also hosts WCF service. there can be many instances of subscriber running on different machines across network. orchestration decides based on the content of StatusMessage which subscriber should the message be routed. What I have done now is, within orchestration I have referenced StatusMessage type and used it as message for both send and receive shapes. my send shape is binded to dynamic send port. I have tried two senarios here. One publishing orchestration as WCF service and other without publishing. In first senario used svcutil to create BizTalkServiceInstance.cs and app.config file. BizTalkServiceInstance.cs for some reason creates shadow type to represent my StatusMessage type. In my WCF client console application (publisher) I call the orchestration service and send shadow type to biztalk. Now my subscriber is waiting for biztalk to call its service and transmit StatusMessage type to its method call. For some reason message arrives upto orchestration Send shape but it is not able to transmit message to subscriber because of conflict in type between orchestration message type and subscriber's StatusMessage type although they are using same type. StatusMessage type is also in GAC. Please can someone advice what I am doing wrong here? How can I make a .net class transmit from one console application to another console application (hosting WCF service) via biztalk orchestration.Anonymous
January 16, 2009
Raj, check your transport type - it should be WCF-BasicHttp.Anonymous
January 16, 2009
Bhavesh, You should try: a) Consuming the WCF service of the 'console publisher' in your BizTalk project (check the steps required here: http://msdn.microsoft.com/en-us/library/bb246019.aspx) b) Then have the second orchestration do the needed routing and expose that as a WCF service and your 'console subscriber' is the client that you build for this service as discussed above.Anonymous
January 18, 2009
The orchestration cannot use consume WCF service wizard because the necessary routing decides address for dynamic send port. I am using StatusMessage type in WCF client - Biztalk - WCF service but still I get following error A message sent to adapter "WCF-NetTcp" on send port "DynamicMessaging_1.0.0.0_DynamicMessaging.Message_Routing_Orchestration_WCFSendMessagePort_d5dd64ad71c30f3c" with URI "net.tcp://localhost:8000/SubscriberWCFPort/MessageReceiver" is suspended. Error details: System.ServiceModel.FaultException: <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/fault</a:Action><a:RelatesTo>urn:uuid:97529b03-af09-421c-af58-000a78194977</a:RelatesTo><a:To s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/anonymous</a:To></s:Header><s:Body><s:Fault><s:Code><s:Value>s:Sender</s:Value><s:Subcode><s:Value>a:ActionNotSupported</s:Value></s:Subcode></s:Code><s:Reason><s:Text xml:lang="en-GB">The message with Action 'SendMessage' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).</s:Text></s:Reason></s:Fault></s:Body></s:Envelope> at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.RequestCallback(IAsyncResult result) MessageId: {0E43E273-77CB-49FE-A015-F6EB03517829} InstanceID: {230D46D4-20E6-4D60-9866-E9EB1640F06A} For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.Anonymous
January 18, 2009
the bizzare thing is if I dont use orchestration and send message through receive location to wcf send port then it works fine. but i want to use orchestration to enable message content based routing.Anonymous
January 19, 2009
Why do you need orchestration to do CBR, you can use filters in your dynamic SendPort? Look at this blog: http://blogs.digitaldeposit.net/saravana/post/2007/04/19/Can-you-use-Dynamic-Send-Port-without-Orchestration-Yes-you-can.aspx RahulAnonymous
April 24, 2009
Great article. Exactly what I was looking for. I use Biztalk 2009 on a virtual machine. Everything worked just fine till the "BizTalk WCF Service Publishing Wizard" part. It generated the webservice directory fine but with no wsdl. The only weird stuff happened "BizTalk assembly file" step of the publishing wizard : there was no default SimpleCreditCheck.dll and I had to go select it in my project bin/debug directory ... Is this where I got it wrong ?Anonymous
April 26, 2009
Strange, I did this on BizTalk 2006 R2 but I'll try it on BizTalk 2009 and let you know. Curious to know what is inside the Web Services directory?Anonymous
April 26, 2009
There is the App_Data folder, the svc file and Web.config App_Data has our input and output xsd files and SchemaIndex.xml, Serialization.xsd and Service Description.xml along with a Temp directory Temp holds a BindingInfo.xml and Wcf ServiceDescription.xml I couldn't figure out which precise Biztalk version is installed on this VM, but Biztalk Server Configuratio reports Version 3.8.104.5 . Maybe I have a beta version ?Anonymous
April 27, 2009
OK, so you can get the WSDL from the .svc file. In IIS Manager, click Content View. In the right pane, right-click the WCF service .svc file that the you created, and then click Browse. This opens IE to display a page that shows you have successfully created a running WCF service. This page also includes a full WSDL address that you can copy and use svcutil.exe to create proxy code for your client. Hope that helps.Anonymous
April 27, 2009
By the way, if the value of the ProductVersion key is greater than 3.6.1404.0, it is BizTalk 2009.Anonymous
April 27, 2009
The comment has been removedAnonymous
April 28, 2009
Thanks for sharing your experiences Etienne. Yes, IIS App Pool should have appropriate permissions, otherwise it would definitely create the kinds of problems that you encountered. I should update my blogs to reflect that.Anonymous
June 11, 2009
Can we change the BizTalkServiceInstance Name in the generated WSDL? If so, which places in servicesdescription.xml we need to edit.Anonymous
June 17, 2009
Hi Ashi, I have not tried this on 2009, but I believe the BTSWsdlExporter does not allow you to change the service name.Anonymous
February 11, 2014
Very Very nice article and descriptive. I salute you. With you this article i have solved my problem. Thanks Man.