Running Header 1
The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft
must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of
any information presented after the date of publication.
The BizTalk Hotrod is for informational purposes only. The BizTalk Hotrod contains the thoughts, theories and experiences to those who work with BizTalk professionally and
voluntarily contribute to its publication. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.
2 Running Header
In one of my recent projects one of the
most important requirements was to
avoid suspended messages at all costs.
Our Customer wanted to have a more
friendly way to deal with exceptions
and the Administration Console with
Suspended Messages and resubmis-
sions was too expensive in terms of
management for the operations team.
They wanted a consistent and com-
mon way to handle exceptions in all
BizTalk applications. I know sometimes
suspended messages are OK but in my
case they were not allowed!
Fortunately by that time I was read-
ing Pro BizTalk, a book which was
just published by Apress and written
by my colleagues George Dunphy
and Ahmed Metwally from Microsoft
Canada. In one of the chapters of the
book they explain the Failed Message
Routing feature available in BizTalk
Server 2006 which deals with sus-
pended messages at the BizTalk Mes-
saging engine (Receive and Send ports)
and the ESB Exception Management
Framework which is a component of
the Patterns and Practices ESB Guid-
ance which deals with failed messages
in the orchestration engine. They were
proposing a uni ed solution to deal
with any suspended message across all
BizTalk applications.
So I decided to build a Common Biz-
Talk Exception Management appli-
cation following this approach. This
common BizTalk application should
process any suspended message and
publish them in a common reposi-
tory for further review, analysis and
eventually resubmission. The solution
had the following components and
characterisitics:
Three orchestrations (One for
receiving failures, one for send-
ing failures and one for processing
failures)
The orchestrations publish the sus-
pended message in the Sharepoint
Library or File System folder and
write an entry in the application
event log
The orchestrations send an email
message to the BizTalk admin with a
link to the suspended message and
the detailed information from the
context properties including error
location and description
The following picture shows the imple-
mented solution
BizTalk Exception Management Application 3
Jean Paul Tabja
Senior Consultant
Microsoft
Cracking open the BizTalk Hood: BizTalk
Failed Message Routing and the ESB
Exception Management Framework
Before going into the details of the developed solution I think
we should crack open BizTalk’s hood and take a brief look at
the basic components that are behind this concept: BizTalk
Failed Message Routing and Pattern and Practices ESB Guid-
ance Exception Management Framework
BizTalk Failed Message Routing
The error handling feature available in BizTalk
Server 2006 and BizTalk Server 2006 R2 allows
the designer to override the original behavior
of placing failed messages in the Suspended
queue. Failed message routing can be enabled
on send ports and receive locations
When failed message routing is activated for a
Receive or Send port the automatic handling
routes an error message to any subscribed des-
tination (send port or orchestration). The error
message is a clone of the original suspended
message with selected properties related to
the speci c messaging failure promoted to the
message context of the new message in the
ErrorReport namespace. The available proper-
ties for a failed message subscription includes:
MessageType
FailureCode
InboundTransportLocation
ReceivePortName
OutboundTransportLocation
SendPortName
Description.
ESB Exception Management Framework
As mentioned before the Failed Message Routing feature
only deals with failures at the messaging level. The ESB Excep-
tion Management Framework provides a similar mechanism
focused on orchestration failures. The ESB Exception Manage-
ment Framework has the following components:
The Failed Orchestration Routing Mechanism
The Custom Exception Encoder Pipeline Component
The ESB Management Portal
The ESB FaultMessage Viewer
The InfoPath Message Template
The ESB Exception BAM Tracking Pipeline Component
You can download the complete ESB Guidance including the
Exception Management Framework from the following link
http://www.codeple x.com/esb
For the Common Exception Management solution imple-
mented in my Project I used only the Failed Orchestration
Routing Mechanism (previously known as the ESB Exception
Management API).
Failed Orchestration Routing MechanismThe Failed Orches-
tration Routing Mechanism is the heart of the ESB Exception
Management Framework and it has two key components:
A BizTalk schema called the FaultMessage
A .NET component which provide the functionality to cre-
ate and consume FaultMessage(s) (or Failed Orchestration
message(s))
4 BizTalk Exception Management Application
The FaultMessage is the equivalent
of the Failed Message from the Failed
Message Routing feature and include
almost the same message properties.
The FaultMessage can embed one or
more messages from the orchestra-
tion and an exception object as well.
In the case of failed Orchestrations
the developer must implement the
FaultMessage construction in an
exception handler block inside every
orchestration that requires this level
of error processing. Once the mes-
sage has been constructed in the
orchestration,the FaultMessage is pub-
lished to the MessageBox using Direct
Binding. The Failed Orchestration
Routing API provides the following
methods.
CreateFaultMessage (creates a fault
message with the correspondent
context properties)
AddMessage (adds an existing
orchestration message to the fault
message)
SetFaultMessageException (adds
an existing exception to a fault
message)
GetMessages (Retrieves all mes-
sages embedded within a fault
message)
BizTalk Exception Management Application 5
//Construct a new Fault Message
FaultMessage = Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.CreateFaultMessage();
//Set properties
FaultMessage.Application = "ApplicationName";
FaultMessage.FailureCategory = "MessageBuild";
FaultMessage.FaultCode = "NNNN";
FaultMessage.FaultDescription = "Some error occured";
FaultMessage.FaultSeverity = Microsoft.Practices.ESB.ExceptionHandling.FaultSeverity.Severe;
FaultMessage.Scope = "Execution Scope";
//Add suspended message (SourceMessage) to the FaultMessage
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.AddMessage(FaultMessage,SourceMessage);
//Add the exception from the exception handler to the FaultMessage
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.SetException(FaultMessage,SysExc);
//Construct a new Fault Message
FaultMessage = Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.CreateFaultMessage();
//Set properties
FaultMessage.Application = "ApplicationName";
FaultMessage.FailureCategory = "MessageBuild";
FaultMessage.FaultDescription = "Some error occured";
FaultMessage.FaultSeverity = Microsoft.Practices.ESB.ExceptionHandling.FaultSeverity.Severe;
FaultMessage.Scope = "Execution Scope";
//Add suspended message (SourceMessage) to the FaultMessage
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.AddMessage(FaultMessage,SourceMessage);
//Add the exception from the exception handler to the FaultMessage
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.SetException(FaultMessage,SysExc);
GetMessage (Retrieves a message of a speci c type
embedded in the fault message)
Publishing the Fault Message
The FaultMessage is constructed and published inside an
orchestration exception handler using a construct shape and
a send port with Direct Binding. It can include one or more
orchestration messages and an exception object.
The following code is implemented inside the Construct
shape to construct the FaultMessage (the implemented meth-
ods from the API are CreateFaultMessage, AddMessage and
SetException).
Processing the FaultMessage
The Failed Orchestration Routing
Mechanism provides the methods to
process the FaultMessage to extract
the embedded messages and the
exception information. The provided
methods are GetMessage and Get-
Messages. These methods are imple-
mented in the common orchestration
which is responsible for processing
the FaultMessages. The common
orchestration is subscribed to the
FaultMessage type. It processes the
FaultMessage to obtain the exception
information and extracts the embed-
ded message(s) to publish them to
the common exception management
repository which can be a Sharepoint
library,  le system, database table, etc.
Note that the publishing of the Fault-
Message is implemented in every
application orchestration but the
processing or consumption of the
FaultMesssage(s) is only implemented
in the Common Exception Manage-
ment solution.
The Solution circuit
Now that we tricked out our engine
with some custom after markets lets
drive through the solution circuit. The
test circuit is designed to validate the
modi cations we’ve made to our Biz-
Talk Environment. Remember we’re
looking to test:
ProcessReceiveFailures
orchestration
ProcessSendFailures orchestration
ProcessOrchestrationFailures
orchestration
6 BizTalk Exception Management Application
Advice to the drivers: Brake at the Subscriptions curve!
The test of our modi cations is  rst curve of the circuit. It deals with subscriptions
and has some debris and oil from a previous race so it was a little bit tricky to handle.
For example we found that not all available properties in the ErrorReport namespace
are promoted in the failed message when an error occurs (according to the BizTalk
Server 2006 and BizTalk Server 2006 R2 documentation in http://msdn2.microsoft.
com/en-us/library/aa5785 16.aspx). The selected properties for each orchestration are
shown in the following table.
Orchestration Subscription
ProcessReceiveFailures ErrorReport.ReceivePortName exists
ProcessSendFailures ErrorReport.SendPortName exists
ProcessOrchestrationFailures (BTS.MessageType ==
“http://schemas.microsoft.biztalk.practices.esb.com/exceptionhandling#FaultEnvelope”)
Orchestration
Subscription
ProcessReceiveFailures
ErrorReport.ReceivePortName exists
ProcessSendFailures
ErrorReport.SendPortName exists
ProcessOrchestrationFailures
(BTS.MessageType ==
“http://schemas.microsoft.biztalk.practices.esb.com/exceptionhandling#FaultEnvelope”)
BizTalk Exception Management Application 7
As you can see from the table the mes-
saging orchestrations are subscribed
to properties from the ErrorReport
namespace and the ProcessOrches-
trationFailures is subscribed to the
MessageType property of the ESB
Excpection Management Framework
FaultMessage.
On your marks get set….GO!
ProcessReceiveFailures
orchestration
As mentioned before the our test
circuit was designed to evaluate the
performance out of aftermarket BizTalk
modi cations. On any test drive you
want a balance of straight lines and
curves. Below are list the highlights of
our test circuit:
Get the message context properties
(Curve)
Get the application name as an
additional error context information
(Curve)
Write the error description in the
application event log (Straight line)
Publish the message in the Share-
point library (Curve)
Send the email message to the
BizTalk administrator with the error
details and link to the error mes-
sage (Straigh line)
We’ll only provide commentary on the
curves (straight lines are too easy!).
Get the message context curve
To obtain the relevant message con-
text properties for a receive failure
the following code was implemented
inside an expression shape.
strReceivePortName = FailedMessage(ErrorReport.ReceivePortName)
strReceiveURI = FailedMessage(ErrorReport.InboundTransportLocation);
strErrorDescription = FailedMessage(ErrorReport.Description)
strReceivePortName = FailedMessage(ErrorReport.ReceivePortName)
strReceiveURI = FailedMessage(ErrorReport.InboundTransportLocation);
strErrorDescription = FailedMessage(ErrorReport.Description)
After this we drive through the application event log at full throttle and prepare
for the next curve.
Get the application name curve
This curve is a little bit tricky because this property is not available in the Error-
Report namespace and it was one of the requirements of the race. Fortunately the
driver has different options to solve this challenge. I decided to take the BizTalk
ExplorerOM path and implemented the following code for the receive failure.
Public Function GetApplicationNameByReceivePort(ByVal ReceivePortName As String) As String
Dim myCatalogExplorer As Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer = New Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer.ConnectionString = "Server=(local);Database=BizTalkMgmtDb;Integrated
Security=true"
Dim myPortCollection As Microsoft.BizTalk.ExplorerOM.ReceivePortCollection
myPortCollection = myCatalogExplorer.ReceivePorts
Dim myPort As Microsoft.BizTalk.ExplorerOM.ReceivePort
myPort = myPortCollection.Item(ReceivePortName)
Dim strApplicationName As String = ""
strApplicationName = myPort.Application.Name
Return strApplicationName
End Function
Public Function GetApplicationNameByReceivePort(ByVal ReceivePortName As String) As String
Dim myCatalogExplorer As Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer = New Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer.ConnectionString = "Server=(local);Database=BizTalkMgmtDb;Integrated
Security=true"
Dim myPortCollection As Microsoft.BizTalk.ExplorerOM.ReceivePortCollection
myPortCollection = myCatalogExplorer.ReceivePorts
Dim myPort As Microsoft.BizTalk.ExplorerOM.ReceivePort
myPort = myPortCollection.Item(ReceivePortName)
Dim strApplicationName As String = ""
strApplicationName = myPort.Application.Name
Return strApplicationName
End Function
Publish the suspended message
to the Exception Management
Repository curve
This curve it’s pretty straight forward
because the message to be published
is the subscribed message and we
only need to implement to code for
a dynamic port using the required
adapter. You can use the Sharepoint
adapter to publish the message to a
Sharepoint library or the FILE adapter
to copy the original message in a
shared folder.
ProcessSendFailures
orchestration
Our second loop on the test circuit will
focus on Orchestration Send Failures.
Get the message context curve
The  rst curve in this lap is the Get-
ContextProperties curve. To obtain the
relevant message context properties
for a send failure the following code
was implemented inside an expression
shape.
8 BizTalk Exception Management Application
strSendPortName = FailedMessage(ErrorReport.SendPortName)
strSendURI = FailedMessage(ErrorReport.OutboundTransportLocation);
strErrorDescription = FailedMessage(ErrorReport.Description)
Get the application name curve
This curve is almost the same as the one already passed in the  rst lap but instead of
going down we are going up! Again I used the Explorer OM approach with the following code
Public Function GetApplicationNameBySendPort(ByVal SendPortName As String) As String
Dim myCatalogExplorer As Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer = New Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer.ConnectionString = "Server=(local);Database=BizTalkMgmtDb;Integrated
Security=true"
Dim myPortCollection As Microsoft.BizTalk.ExplorerOM.SendPortCollection
myPortCollection = myCatalogExplorer.SendPorts
Dim myPort As Microsoft.BizTalk.ExplorerOM.SendPort
myPort = myPortCollection.Item(SendPortName)
Dim strApplicationName As String = ""
strApplicationName = myPort.Application.Name
Return strApplicationName
End Function
strSendPortName = FailedMessage(ErrorReport.SendPortName)
strSendURI = FailedMessage(ErrorReport.OutboundTransportLocation);
strErrorDescription = FailedMessage(ErrorReport.Description)
Public Function GetApplicationNameBySendPort(ByVal SendPortName As String) As String
Dim myCatalogExplorer As Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer = New Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
myCatalogExplorer.ConnectionString = "Server=(local);Database=BizTalkMgmtDb;Integrated
Security=true"
Dim myPortCollection As Microsoft.BizTalk.ExplorerOM.SendPortCollection
myPortCollection = myCatalogExplorer.SendPorts
Dim myPort As Microsoft.BizTalk.ExplorerOM.SendPort
myPort = myPortCollection.Item(SendPortName)
Dim strApplicationName As String = ""
strApplicationName = myPort.Application.Name
Return strApplicationName
End Function
Publish the suspended message to the Sharepoint
library curve
This is almost a replay of the curve from the  rst lap!
We are ready to go for the third and  nal lap of the circuit!
Sector 3: ProcessOrchestrationFailures
orchestration
The  nal lap of the circuit is the ProcessOrchestrationFailures
and although it has the same structure requires a little bit
more of concentration from the drivers to avoid any danger-
ous mistake! First of all in this lap you need to have a refer-
ence to the ESB Exception Management Framework API
and to the FaultMessage schema otherwise you will be black
agged and out the race!
Get the message context properties and application
name curve
The context properties including the application name are
obtained from the FaultMessage body and from the embed-
ded exception. To obtain the embedded exception we use the
GetException method from the ESB Exception Framework
component. The code to obtain all the context properties is
shown below.
‘Get the exception from the FaultMessage and obtain the error description
sysException = Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.GetException(FaultMessage);
strDescription = sysException.Message
‘Get the FaultMessage properties
strApplicationName = FaultMessage(Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property
Application)
strScope = FaultMessage(Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property.Scope)
‘Get the exception from the FaultMessage and obtain the error description
sysException = Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.GetException(FaultMessage);
strDescription = sysException.Message
‘Get the FaultMessage properties
strApplicationName = FaultMessage(Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property
Application)
strScope = FaultMessage(Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property.Scope)
Publish the suspended message to the Exception
Management Repository curve
You might believe that this will be the easiest curve of the
circuit but you’d be wrong. You need to know how to deal
with ESB Exception Management Framework API. The source
orchestration (The one that had the problem) wrapped the
suspended message(s) in the FaultMessage. So now is time to
do the opposite.
The  rst step is to retrieve the collection of embedded mes-
sages using the GetMessages method with the following code
inside an expression shape.
The second step is to obtain all messages using a loop (with the condition mes-
sageCollection.MoveNext()). The following picture shows the processing loop to
get all embedded messages.
Inside the loop the following code is used to get each embedded message
(TempMessage has a type of System.Xml.XmlDocument).
TempMessage = messageCollection.Current;
Each TempMessage is sent to the Sharepoint library using a dynamic send port
with the Sharepoint adapter.
This was the last curve and we are arriving to the Finish Line!
TempMessage = messageCollection.Current;
BizTalk Exception Management Application 9
//Retrieve messages
messageCollection =
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.GetMessages(FaultMessage);
//Retrieve messages
messageCollection =
Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.GetMessages(FaultMessage);
Finish Line
Finally we arrived to the Finish Line with a record lap time!
Hopefully we did a good job for the Team. I think we did.
With the presented approach you obtain the bene t of hav-
ing a single application in your BizTalk Group dealing with
suspended messages. Administrators have a consistent an
easy way to deal with these messages and take the appropri-
ate actions. The only requirement for the developer at design
time is to implement the exception handling code with the
ESB Exception Management Framework and to turn the
Enable Routing of Failed Messages property in the messaging
ports for the bindings.
Sample code
I have provided a sample business application (POApplication)
and a sample Common Exception Management application.
The POApplication receives a purchase order and converts
the US currency amount to Canadian currency and updates
the purchase order before delivering it to the target system.
To make the sample work you need to perform the following
steps:
Copy the Samples folder to your C: drive
Install the Common Exception Management BizTalk
application
Start the Common Exception Management application
Install the POApplication BizTalk application
Start the POApplication
Testing the sample application
Three test cases are provided in the sample. The  rst case
has no errors and the output should go the de ned folder
MsgPOOut. The second test case is a receive failure and in
this case the source message should go the Failed Message
Repository which is a folder inside the Common Exception
Management directory (C:\Samples\Common\Repository).
The third test case is an orchestration failure (due to a divided
by zero error) and as in the second case the source message
goes to the Failed Message Repository.
Hope you enjoyed the race!
10 Running Header
BizTalk Server is a truly comprehen-
sive platform that includes many very
powerful components. One of the
perhaps less utilized components is
the Business Rules Engine (BRE). This
article tries to provide an alternative
approach to accessing the BRE. The
goal is to help BizTalk developers to
understand how to leverage the Appli-
cation Programming Interface (API) of
the BizTalk BRE and expose the policy
execution process as Windows Com-
munication Foundation (WCF) services.
This opens up new opportunities for
leveraging BRE independent of BizTalk
orchestration.
BRE in BizTalk
Most of BizTalk developers are familiar
with calling business rules from within
a BizTalk orchestration. This is accom-
plished usually by dropping a “Call
Rules” shape onto the orchestration
designer surface, select a policy that is
deployed in the rules store, and pick
out message parameters to send to
the rule set for evaluation. Once the
orchestration is deployed, BizTalk will
automatically look for the latest ver-
sion of the policy and apply for evalu-
ation when the orchestration instance
is launched. The added benet is of
course you can update the rule sets in
the policy independent of the BizTalk
application, and you can rest assured
that the latest version of the policy is
always going to be executed.
Hai Ning
Product Technical Specialist
BPM/SOA
While this is the most common way in
leveraging the rules engine, it is cer-
tainly not the only way. For instance,
there might be times that you want
to call a speci c version of policy rule
within Orchestration, not always the
latest version. Or, you might want to
call the policy from an external appli-
cation that runs in its own process
independent of BizTalk. After all, the
great promise of a rules engine is to
provide a centralized location for stor-
ing and executing abstracted business
logic. This can apply to many busi-
ness applications and scenarios. For
example, insurance companies might
choose to use BRE to store insurance
policies, which are essentially large
sets of complex rules. These insurance
policies can be called upon within their
BizTalk application to process insur-
ance claims; but they might also be
executed from an ASP.NET web appli-
cation, a Microsoft Excel spreadsheet,
or even a J2EE application. Having
these policies implemented in differ-
ent environments, using different tools
and deployed into different systems
could be a nightmare for develop-
ment, deployment and maintenance.
Fortunately, BizTalk BRE provides a set
of APIs to expose its functionalities
so that they can be consumed by any
external .NET application. With a little
WCF magic salt, we can even turn the
policies into WCF services that can be
served to WCF client applications or
even non-.NET client applications.
BRE API
Most of the API objects and methods
we are going to explore in this article
are exposed through this one par-
ticular DLL: Microsoft.RuleEngine.
dll, which comes with BizTalk Server
installation.
Exposing Business Rules Engine Policies as WCF Services 11
You can  nd this DLL in the following
directory if you are running BizTalk
2006 R2: “C:\Program Files\Common
Files\Microsoft BizTalk. However, if
you are still running earlier versions of
BizTalk Server (2004 or 2006), it is in a
different location at “C:\Program Files\
Microsoft BizTalk Server 2004(6)\.
This is one of the gotcha’s when you
upgrade to R2; remember to update
your rule engine reference in your
Visual Studio project.
Example Scenario
Let’s use a simple example to illustrate this approach. I have a policy named
OrderApprovalPolicy, version 1.1, deployed in my BizTalk server SQL rule store. This
simple policy contains two rules, Approve and Reject.
For the rule Approve, I am simply looking at the value of the TotalAmount  eld of
a SalesOrder XML schema. If the value is greater than or equal to 300, I will set
the value of Status  eld to the output of a function named ApproveOrder() from a
.NET object StatusUpdater.
This StatusUpdater class is already deployed in my local GAC. It is only a dummy
class that has two methods; each returns a string value in “Approved” or “Rejected
respectively. This is simply to demonstrate the point of how to add a .NET class
fact to my rule set in Business Rules Composer as well as how to do that same later
in my .NET code. It obviously did not add a great deal of value to this particular
policy. But you could imagine having much more sophisticated logic implemented
in a .NET class and add it to the rule set.
[Serializable]
public class StatusUpdater
{
public StatusUpdater() { }
public static string ApproveOrder() { return "Approved"; }
public static string RejectOrder() { return "Rejected"; }
}
For the rule Reject, I simply set the status to the output of RejectOrder() method
when the value of the TotalAmount  eld is less than 300.
[Serializable]
public class StatusUpdater
{
public StatusUpdater() { }
public static string ApproveOrder() { return "Approved"; }
public static string RejectOrder() { return "Rejected"; }
}
12 Exposing Business Rules Engine Policies as WCF Services
And here is my SalesOrder XML
schema fact, as well as my StatusUp-
dater .NET class fact.
Of course, calling this policy
from an orchestration is
very straightforward.
Execute the Policy in .NET Code
To execute this policy in your .NET
code, you simply create a new con-
sole application project inside Visual
Studio 2005, add a reference to the
Microsoft. RulesEngine.dll to your
.NET application, and create a new
instance of the Policy object. The
constructor of the Policy class can just
take the name of the deployed policy,
which will automatically load the lat-
est version of that policy; or addition-
ally you can specify major and minor
version numbers to load a particular
version of the deployed policy. Once
the policy is constructed, you need to
supply an object array that contains all
the facts the rule set needs for evalu-
ation. These facts can be an instance
of a .NET class, an XML document, or
even a DataConnection object. Each
of these corresponds to one of the
types of facts that you can create using
the Business Rules Composer user
interface.
One thing to pay special attention to is that you must use the TypedX-
mlDocument class from the Microsoft.RulesEngine namespace when
supplying an XML document as a fact. This is a wrapper class that wraps
an XmlDocument object with a DocumentType property, among other
things. Typically this DocumentType property, which you need to supply
to the constructor of the TypedXmlDocument object, takes the format
of [BizTalk Project Name].[Schema Name]. You can observe the value of
this  eld in the Document Type  eld in the Properties of the XML schema
from the Business Rules Composer.
Exposing Business Rules Engine Policies as WCF Services 13
This  eld can be automatically populated by the
Fact Explorer if you create the XML Document
fact by browsing to an existing xsd  le living
inside a Visual Studio BizTalk project. However,
if this xsd  le is not part of a BizTalk project, this
[BizTalk Project Name] part of the value will be
left blank and you will need to manually  ll it in.
Otherwise, you will  nd your rule execution fails
at run time with the rules not found exception,
even though the Test Policy function works  ne
inside the Rules Composer. So here is the code
snippet that executes the policy in C# code.
Note that I also added a DebugTrackingInter-
ceptor object to, well, intercept the debug
tracking information. This will essentially cre-
ate a log  le (trace le.txt) that contains all the
execution information of the policy.
public bool IsOrderApproved()
{
DebugTrackingInterceptor interceptor =
new DebugTrackingInterceptor("tracefile.txt");
Policy orderPolicy = new Policy("OrderApprovalPolicy", 1, 1);
List<object> facts = new List<object>();
facts.Add(new StatusUpdater());
XmlDocument orderDoc = new XmlDocument();
orderDoc.Load("MyOrder.xml");
TypedXmlDocument xmlOrder =
new TypedXmlDocument("BRESample.BtsApp.SalesOrder", orderDoc);
facts.Add(xmlOrder);
orderPolicy.Execute(facts.ToArray(), interceptor);
string status =
xmlOrder.Document.SelectSingleNode("//Status").InnerText;
return status == "Approved" ? true : false;
}
public bool
IsOrderApproved()
{
DebugTrackingInterceptor
interceptor =
new
DebugTrackingInterceptor
(
"tracefile.txt"
);
Policy
orderPolicy =
new
Policy
(
"OrderApprovalPolicy"
, 1, 1);
List
<
object
> facts =
new
List
<
object
>();
facts.Add(
new
StatusUpdater
());
XmlDocument
orderDoc =
new
XmlDocument
();
orderDoc.Load(
"MyOrder.xml"
);
TypedXmlDocument
xmlOrder =
new
TypedXmlDocument
(
"BRESample.BtsApp.SalesOrder"
, orderDoc);
facts.Add(xmlOrder);
orderPolicy.Execute(facts.ToArray(), interceptor);
string
status =
xmlOrder.Document.SelectSingleNode(
"//Status"
).InnerText;
return
status ==
"Approved"
?
true : false
;
}
One interest fact worth mentioning is that BRE does not validate the XML docu-
ment fact based on the schema. The reason is that the rules are stored in BRML
(Business Rules Modeling Language) format in the SQL database. And it only
contains the document type, the name space of the XML schema, the  le path to
the original XML schema  le which may or may not exist on the server the rule is
deployed, and the xpaths of the  elds of the schema that are required by the rule
execution. But it does not store the schema itself. This means as long as the XML
document fact contains the necessary  elds which can be located by the xpaths
recorded in the rule set, BRE does not care about the rest of the  elds. For exam-
ple, my “MyOrder.xml”  le I used in the above code actually looks like the follow-
ing. It is obviously not compliant to the SalesOrder schema we used to create the
rule. But the rule executes without problem.
<ns0:Order xmlns:ns0="http://BRESample.BtsApp.SalesOrder">
<OrderId>101</OrderId>
<Customer>
<Name>Anakin Skywalker</Name>
<!--Address tag is missing.-->
</Customer>
<PhoneNo>617-231-2342</PhoneNo>
<!--PhoneNo tag is not defined in the schema.-->
<LineItems>
<LineItem>
<ProductId>Lightsaber</ProductId>
<Qty>12</Qty>
<Price>25.12</Price>
</LineItem>
</LineItems>
<TotalAmount>301.4</TotalAmount>
<Status>Initiated</Status>
</ns0:Order>
<
ns0:Order
xmlns:ns0
=
"
http://BRESample.BtsApp.SalesOrder
"
>
<
OrderId
>
101
</
OrderId
>
<
Customer
>
<
Name
>
Anakin Skywalker
</
Name
>
<!--
Address tag is missing.
-->
</
Customer
>
<
PhoneNo
>
617-231-2342
</
PhoneNo
>
<!--
PhoneNo tag is not defined in the schema.
-->
<
LineItems
>
<
LineItem
>
<
ProductId
>
Lightsaber
</
ProductId
>
<
Qty
>
12
</
Qty
>
<
Price
>
25.12
</
Price
>
</
LineItem
>
</
LineItems
>
<
TotalAmount
>
301.4
</
TotalAmount
>
<
Status
>
Initiated
</
Status
>
</
ns0:Order
>
14 Exposing Business Rules Engine Policies as WCF Services
I often see people using objects in the
System.Xml namespace or worse—
string concatenation—to build the
XML document before feeding to
the policy execution. A much bet-
ter approach, when you execute the
policy from .NET code, is to speak in
the language of objects rather than
XML  les. You don’t have to write a
single line of code for constructing the
entity class. There is where our friend
xsd.exe can help. Using xsd.exe, we can
easily translate an XSD schema into a
C# object.
xsd.exe SalesOrder.xsd /classes /namespace:BRESample.WcfService
xsd.exe SalesOrder.xsd /classes /namespace:BRESample.WcfService
The above command line generates
SalesOrder.cs  le that contains an
Order class with all the XML serializa-
tion attribute decoration applied to
the class and properties. It also puts
the class under the BRESample.Wcf-
Service namespace and decorated
with “partial” key word. All that is left
to do is to add a pair of serialization
and de-serialization methods to this
class in a different source code  le
so we don’t overwrite these methods
if we have to regenerate the source
code again(Don’t you love the partial
class?) And we can now easily create a
TypedXmlDocument from a SalesOrder
or vice versa. Following is the code for
serializing and de-serializing the Order
object.
public partial class Order
{
public TypedXmlDocument Serialize()
{
XmlSerializer xs = new XmlSerializer(typeof(Order));
XmlDocument doc = new XmlDocument();
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, this);
ms.Position = 0;
doc.Load(ms);
TypedXmlDocument typedXmlDoc =
new TypedXmlDocument("BRESample.BtsApp.SalesOrder", doc);
return typedXmlDoc;
}
public static Order Deserialize(TypedXmlDocument typedXmlDoc)
{
byte[] data = ASCIIEncoding.Unicode.GetBytes(
typedXmlDoc.Document.OuterXml);
MemoryStream ms = new MemoryStream(data);
XmlSerializer xs = new XmlSerializer(typeof(Order));
Order order = (Order)xs.Deserialize(ms);
return order;
}
}
public partial class
Order
{
public
TypedXmlDocument
Serialize()
{
XmlSerializer
xs =
new
XmlSerializer
(
typeof
(
Order
));
XmlDocument
doc =
new
XmlDocument
();
MemoryStream
ms =
new
MemoryStream
();
xs.Serialize(ms,
this
);
ms.Position = 0;
doc.Load(ms);
TypedXmlDocument
typedXmlDoc =
new
TypedXmlDocument
(
"BRESample.BtsApp.SalesOrder"
, doc);
return
typedXmlDoc;
}
public static
Order
Deserialize(
TypedXmlDocument
typedXmlDoc)
{
byte
[] data =
ASCIIEncoding
.Unicode.GetBytes(
typedXmlDoc.Document.OuterXml);
MemoryStream
ms =
new
MemoryStream
(data);
XmlSerializer
xs =
new
XmlSerializer
(
typeof
(
Order
));
Order
order = (
Order
)xs.Deserialize(ms);
return
order;
}
}
Create the WCF Service
Our work is nearly done. All that is left is to create a WCF service to expose this
rule execution logic for service clients to consume. First, we de ne the service
contract. We can choose to create a contract with simple types as input and out-
put, such as the below IsOrderApproved method. We can also choose to use the
complex type Order, which is already created for us by the xsd.exe as a serializable
type. We don’t even need to decorate it with the DataContract and DataMember
attribute since it is already XML-serializable. Thanks again, xsd.exe.
Exposing Business Rules Engine Policies as WCF Services 15
[ServiceContract]
interface IOrderService
{
[OperationContract]
bool IsOrderApproved(decimal totalAmount);
[OperationContract]
Order ProcessOrder(Order order);
}
[ServiceContract]
interface IOrderService
{
[OperationContract]
bool IsOrderApproved(decimal totalAmount);
[OperationContract]
Order ProcessOrder(Order order);
}
The last step is to cre-
ate the service endpoint
and the service host.
Here I am showing an
example of hosting this
rule engine policy WCF
service over WS-HTTP
binding at http://local-
host:9000/policy. I also
enabled the meta data
so my WCF client can
get hold of the WSDL to
build the proxy classes.
Then we create the service implemen-
tation using our serialization helper
methods. Here is the implementation
of the ProcessOrder method where the
policy execution is called. Note that
I choose not to expose the StatusUp-
dater object as part of the contract
but simply add an instance to the fact
object array. But if we want to have
an instance of it as an additional input
parameter for my service contract, I
could do so just as easily since Sta-
tusUpdater class is already Serializable.
class OrderService : IOrderService
{
public Order ProcessOrder(Order order)
{
Policy orderPolicy = new Policy(“OrderApprovalPolicy”,
1, 1);
List<object> facts = new List<object>();
facts.Add(new StatusUpdater());
TypedXmlDocument xmlOrder = order.Serialize();
facts.Add(xmlOrder);
orderPolicy.Execute(facts.ToArray());
order = Order.Deserialize(xmlOrder);
return order;
}
}
class OrderService : IOrderService
{
public Order ProcessOrder(Order order)
{
Policy orderPolicy = new Policy(“OrderApprovalPolicy”,
1, 1);
List<object> facts = new List<object>();
facts.Add(new StatusUpdater());
TypedXmlDocument xmlOrder = order.Serialize();
facts.Add(xmlOrder);
orderPolicy.Execute(facts.ToArray());
order = Order.Deserialize(xmlOrder);
return order;
}
}
static void Main(string[] args)
{
Uri uri = new Uri(“http://localhost:9000”);
using (ServiceHost sh = new ServiceHost(typeof(OrderService), uri))
{
WSHttpBinding b = new WSHttpBinding();
sh.AddServiceEndpoint(typeof(IOrderService), b, “policy”);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
sh.Description.Behaviors.Add(smb);
sh.Open();
Console.WriteLine(“Order approval service is running...”);
Console.ReadKey();
}
}
static void Main(string[] args)
{
Uri uri = new Uri(“http://localhost:9000”);
using (ServiceHost sh = new ServiceHost(typeof(OrderService), uri))
{
WSHttpBinding b = new WSHttpBinding();
sh.AddServiceEndpoint(typeof(IOrderService), b, “policy”);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
sh.Description.Behaviors.Add(smb);
sh.Open();
Console.WriteLine(“Order approval service is running...”);
Console.ReadKey();
}
}
I will skip the steps of creating WCF client. But using svcutil.exe tool, you can eas-
ily generate the stubs and also a client version of the implementation of the Order
class. Here is a screenshot of executing the WCF client calling the policy against 10
randomly generated order objects.
16 Exposing Business Rules Engine Policies as WCF Services
Summary and Next Steps
So there you have it, an end-to-end example of exposing Business Rules Engine
policy as WCF service. If you are interested in playing with the code, you can
download the entire project in ZIP format at my blog site http://www.hyperbina.
com/blog/ ?p=36.
As it is demonstrated in this article, it is fairly easy to expose your Business Rules
Engine policies as WCF services for the external application to consume. So next
time when you are planning to use rules in you BizTalk application, think outside
the BizTalk box. The Business Rules Engine, even though shipped as a component
of BizTalk Server, it is meant to be leveraged as a centralized rules repository for
the whole enterprise application environment to consume. And the best way to
achieve that is to create a WCF service layer on top it so that it can be accessed by
BizTalk orchestration and external client applications.
In fact, if you nd yourself constantly engaged in creating WCF layers for your
business rules, you might want to design an automated mechanism to publish the
BRE policies as WCF services, (Rules to WCF Wizard, anyone?). It can be achieved
through code generation based on XML schemas using classes from System.Xml.
Serialization, Microsoft.CSharp, and System.CodeDom namespaces, as well as Sys-
tem.Reection for the GAC’d assemblies. This is beyond the scope of this article,
but that will be an interesting project for anyone who wants to explore further in
that direction.
About the Author
Hai Ning is a Microsoft Technology Specialist in the New England District. He has
over ten years of experience in developing Microsoft integration solutions for large
and small businesses. He lives in Cambridge, Massachusetts and contributes regu-
larly to the local .NET developer communities. He holds MCSD, MCDBA, MCTS –
SQL Server and MCTS – BizTalk. He can be reached at hai.ning@microsoft.com.
Running Header 17
Back to the sidebar with you, young man! Yes, after
diving deep into the world of BizTalk services in our
last MEGA-issue of BizTalk Hotrod, it’s time to get back
to essentials: namely, evaluating the tools and add-ons
available, either from Microsoft or the enthusiastic Biz-
Talk developer community, to help make your life as a
BizTalk mechanic that much easier. With that in mind,
we’ll be looking at a Convoy Subscription Viewer, a
custom WCF Adapter (for you pre-R2 peeps), and a
Suspended Message Viewer Using WMI. Enjoy!
Convoy Subscription Viewer Tool
http://www.biztalkgurus.com/ les/folders/biztalk_tools/
entry15498.aspx
Installation
Documentation
Samples
Ease of Use
Hero Factor
The Lowdown: Are you implementing convoy pat-
terns in your BizTalk solutions on a regular basis? Are
you tired of having to hunt through mercilessly long
lists of messages and events in HAT? Are you
now, or have you ever been, a member of
the Communist party?... Sorry, scratch that
channeling McCarthy there for some reason.
Anyhow, if you are trying to quickly get a
snapshot of the health of your BizTalk sub-
scriptions that use the convoy pattern, there
is a quick (and not particularly dirty) tool to
help do just that.
Don’t let the apparently mediocre ratings
fool you. This may be just what you are look-
ing for, particularly if you need to unit test assem-
blies that are using convoys. This tool is made with
but a single purpose in mind: To spit out details
about messages in convoys.
The Upside: The one thing this tool does, it does
exceedingly well. It also supports a very clean inter-
face with essentially one operable control… the
“Go” button (okay, so it says “Load, no big-
gie). At the same time, it gives a snapshot of
messages in process in any and all convoys in
your BizTalk Server. This includes information
about the subscribing orchestration, and the
message itself. Extending the app to enhance
its capabilities is facilitated by the fact that full
source code is included in the download.
The Downside: You’re going to need that source
code. There’s no compiled distributable to
just “run”. You need to open the solution
le in the VS IDE and build/run it. Also,
there’s no “admin” capability built in to
the application, so if you need to point it to a
non-default environment you have to change con-
nection strings and SQL query parameters more or
less by hand and then recompile. The  nal problem
(potentially) is that it provides its information by
establishing a link
(via a .NET SQL
Adapter) directly
into some mis-
sion-critical Biz-
Talk tables in SQL.
Consequently, this
is not a tool that
any self-respect-
ing IT Adminis-
trator is going to
want anywhere
The Lowdown:
Are you implementing convoy pat-
Installation
Documentation
Samples
Ease of Use
Hero Factor
Andy Schwarz
Magenic Technologies
MCTS - BizTalk 2004 & 2006
New Bling for your ride
establishing a link
(via a .NET SQL
Adapter) directly
into some mis-
sion-critical Biz-
Talk tables in SQL.
Consequently, this
is not a tool that
any self-respect-
ing IT Adminis-
trator is going to
want anywhere
18 Tools of the Trade
near his production server(s). They do provide a readme  le,
but essentially consists of the very helpful guidance of “just
run it. There are no examples, demos, tips, or test cases pro-
vided to allow the novice to run this thing through its paces.
WCF Adapter for BizTalk Server 2006
http://www.codeple x.com/WCFBizTalk
Installation
Documentation
Samples
Ease of Use
Hero Factor
The Lowdown: Not everyone has the bene t of working
for an employer that sees the value of moving from BizTalk
Server 2006 to BizTalk Server 2006 R2. Yet somehow, they
still expect the technologists (re: you) to make what you do
have interoperate with all the cool stuff that everybody else
has. If that’s your situation, take heart! Out at CodePlex there
is an adapter that will allow you to interoperate with partners
communicating via WCF. The adapter itself offers a feature
set and property pages very akin to the WCF_BasicHTTP
adapter available in R2.
The Upside: Setup is very simple. Simply drop the three
compiled assemblies that come with the download into the
GAC, run a .REG  le (also provided in the download) and
then setup the adapter in the BizTalk Administration Console.
And don’t worry, even if you don’t have this article open in
front of you after downloading the WCF Adapter materials
(although… why wouldn’t you?), these instructions are pro-
vided in the download (as well as on the download site) in a
readme  le. Once con gured, the adapter’s property page
is very familiar in form and structure to other adapters in
general, and to the aforementioned WCF adapter in R2 in
particular.
In fact, in some ways, this
adapter is much easier to
deal with as all major ele-
ments of the adapter’s
con guration are provided
on a single property page,
simply grouped into different
sections.
The Downside: Beyond that,
no other documentation is
provided. No samples, demo, examples, or run-throughs are
provided. The  nal drawback (potentially) is one of cover-
age. If you need to interact with WCF in some way other
than the one available through this adapter (R2 provides no
less than four distinct adapters out of the box) you may be
a little stuck… without diving into the Adapter Framework
and building your own… of course, the Adapter Framework
comes with R2, so… why not just use one of the other adapt-
ers.
The Lowdown:
Not everyone has the bene t of working
Installation
Documentation
Samples
Ease of Use
Hero Factor
Tools of the Trade 19
Suspended Message Viewer Using WMI Tool
http://www.biztalkgurus.com/ les/folders/biztalk_tools/entry15495.aspx
Installation
Documentation
Samples
Ease of Use
Hero Factor
Installation
Documentation
Samples
Ease of Use
Hero Factor
The Lowdown: From the folks who brought you the Convoy
Subscription Viewer Tool comes a diagnostic utility with even
less interactivity! The Suspended Message Viewer Using WMI!
In fact, to call this a “viewer” or a “tool” is to be a bit mislead-
ing. It is a very useful code snippet, at least at its core, which
has been implemented as a command line utility. The core of
what it delivers is encapsulated in the following:
// Print out the service instance ID and error description upon receiving of the suspend event
Console.WriteLine("A MSBTS_ServiceInstanceSuspendEvent has occurred!");
Console.WriteLine(string.Format("ServiceInstanceID: {0}", e.NewEvent["InstanceID"]));
Console.WriteLine(string.Format("ServiceClass: {0}", e.NewEvent["ServiceClass"]));
Console.WriteLine(string.Format("ErrorDescription: {0}", e.NewEvent["ErrorDescription"]));
Console.WriteLine(string.Format("ServiceStatus: {0}", e.NewEvent["ServiceStatus"]));
// Print out the service instance ID and error description upon receiving of the suspend event
Console
.WriteLine(
"A MSBTS_ServiceInstanceSuspendEvent has occurred!"
);
Console
.WriteLine(string.Format(
"ServiceInstanceID: {0}"
, e.NewEvent[
"InstanceID"
]));
Console
.WriteLine(string.Format(
"ServiceClass: {0}"
, e.NewEvent[
"ServiceClass"
]));
Console
.WriteLine(string.Format(
"ErrorDescription: {0}"
, e.NewEvent[
"ErrorDescription"
]));
Console
.WriteLine(string.Format(
"ServiceStatus: {0}"
, e.NewEvent[
"ServiceStatus"
]));
Essentially, the code in the utility sets up a WMI event watcher, which listens to
MSBTS_ServiceInstanceSuspendedEvent for any changes. When one occurs, it
dumps to the console as much detail about the event that it can… very handy for
diagnostic purposes.
The Upside: Simplicity. Just run this utility and watch the suspended message data
pour in.
The Downside: Well, if you’re looking for robust, feature rich tools, or a diagnostic
IDE, you’ve come to the wrong place (honestly, it says WMI right in the title… what
did you expect exactly?). There’s nothing here that you can’t get straight from
HAT or the Administrative Console, save for the complete LACK of an IDE (which, if
you’re trying to do as a silent option, perhaps on multiple server could be a posi-
tive boon. There’s no documentation to speak of (a readme  le with nothing in it
save a disclaimer hardly quali es), no samples, no demos, no test cases, no tips, no
help  le, and no install package all help to emphasize the… self-help nature of this
offering.
Well, that’s it for now. Check out next quarter’s issue where we’ll be taking a  rst
peek at R2’s HL7 capabilities…
20 Running Header
One of the benets of BizTalk is that it allows a developer to accommo-
date complex messaging patterns. To demonstrate how parallel convoys
can be implemented within BizTalk we’ll borrow a scenario from the
health care industry. When a claim is submitted, there are multiple types of valida-
tion that happens (and by no means is this a comprehensive list): Is this a valid con-
tract? Is the hospital part of the network? Is the procedure that was performed
covered? We’ll take two of those for our scenario.
Using loosely coupled orchestrations we can create an orchestra-
tion for each one of the steps above. In this case, both the Validate
Hospital Information and Validate Contract Information receive shapes will have
lters that are looking for messages matching the Claim schema with a status of
CLAIM RECEIVED.
It’s worth discussing the messaging engine of BizTalk here. When an orchestration
is deployed to the BizTalk database, it is necessary to enlist the orchestration and
then start it. Enlisting the orchestration denes the subscription for the orchestra-
tion and BizTalk can form a queue of messages for the orchestration to consume.
When the orchestration is started, it will then begin consuming the messages that
are in the queue. When a message is received, BizTalk examines the message’s
context properties and sees what subscriptions match them. Each orchestration
with a subscription that matches the context properties, so when the ReceiveClaim
Receive
Claim
Process
Claim
Validate
Hospital
Information
Validate
Contract
Information
Guaranty Bank
Parallel Convoys in BizTalk 21
orchestration writes a message to the messagebox with a Status of
CLAIM RECEIVED, the message is placed in the queue for the Vali-
dateHospitalInformation orchestration and in the queue for the
ValidateContractInformation orchestration.
The ProcessClaim orchestration requires input from both the Vali-
dateContractInformation and ValidateHospitalInformation orches-
trations and needs to ensure that the results it uses are for the
same claim. To accomplish this, we de ne a correlation set based
on ValidationClaimID.
This is what ensures that BizTalk consumes both the contract and
hospital validation results for claim 123 together rather than the
contract validation result from claim 234 and the hospital valida-
tion result from claim 345. Correlation sets can only be based on
promoted properties, so the ClaimValidation schema has two pro-
moted properties – ValidationClaimID and ValidationStatus. The
ValidationStatus is used for the  lter condition of the two receive
shapes shown below, and also note that both receive shapes refer-
ence the ClaimIDCorrelationSet.
22 Parallel Convoys in BizTalk
Below are the properties for the two receive shapes.
Another bene t of the correlation set we de ned is that should one of the valida-
tion orchestrations take longer than the other to run, the output from the com-
pleted validation orchestration will be held until it can be paired with the output
from the other validation orchestration, though this will not affect other instances
of the messages or orchestrations. To demonstrate this, I’ve stopped the Validate-
ContractInformation orchestration but the ValidateHospitalInformation is still run-
ning. After submitting a claim, we can view the messages.
The output from the ValidateHospitalInformation orchestration is waiting to be
paired with output from the ValidateContractInformation orchestration. When the
ValidateContractInformation orchestration is started and the suspended instance
is resumed, the ProcessClaim orchestration will consume the output from both
orchestrations and complete the process.
Introduction
Windows Communication Foundation
is a new breed of communications
infrastructure built around web ser-
vices. It is highly fl exible for integrat-
ing with diverse technologies and
can provide superior performance
when it is used on both ends of the
pipe. BizTalk Server 2006 R2 includes
a suite of adapters that utilize WCF.
Not surprisingly since it is a new tech-
nology, one of the most frequently
asked questions I hear about the
WCF adapters is “Can they speak with
non-Microsoft or late model .NET
services (ASMX)?” The answer is “Yes,
but don’t you want to upgrade your
services to WCF?” The WCF-NetTcp
adapter is the preferred adapter for
bridging the gap from BizTalk to
WCF because it allows you to realize
signifi cant performance benefi ts by
bypassing the whole HTTP layer and
communicating directly over TCP.
Russell Waltz
Consultant
CTS, Inc.
24 A Glimpse of the BizTalk Server 2006 R2 WCF Adapters
Since this is an ASMX service, you can invoke it via the WCF-
BasicHttp adapter. The  rst thing you need to do is run the
WCF service consuming wizard. You are not required to use
the wizard to use WCF adapters, but it is usually a starting
point to build a schema for consuming the service. Open the
wizard by right clicking on the BizTalk project to which you
would like to add the schema, click Add, and then click Add
Generated Items. In the following dialog select Consume
WCF Service, and click Add.
It uses a proprietary binary encoding; hence, you can only
use it when WCF is on the other end of the line. Until you
upgrade more of your communications infrastructure to WCF,
you will probably need to use the WCF-WSHttp and WCF-
BasicHttp adapters a lot. The WCF-WSHttp is for communica-
tion via WS-* speci cations (WS-Security and WS-Transaction)
and the WCF-BasicHttp adapter is for communicating via the
older WS:Basic Pro le 1.1. Luckily, since you are using BizTalk,
you can upgrade your communications infrastructure to WCF
at any time without having to change or recompile your Biz-
Talk components. Just switch the the WCF-NetTcp adapter
and go. The intent of this article is to show how you can
communicate with an ASMX service while enjoying the rich
WCF functionality using the WCF-BasicHttp adapter.
Scenario
Let’s consider a simple scenario. An insurance company has
developed an automated group policy rate quote service.
Customers can send employee data to this service to receive
a quote.
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
namespace InsuranceWS
{
/// <summary>
/// Generates a quote for a insurance group rate.
/// </summary>
[WebService(Namespace = "http://InsuranceWS.com/")]
public class Quote : System.Web.Services.WebService
{
[WebMethod]
public decimal GetQuote(string companyName, Group customers)
{
decimal quote = customers.people.Length * 100;
return quote;
}
}
/// <summary>
/// Represents the people in the group.
/// </summary>
public class Group
{
public Person[] people;
}
/// <summary>
/// Represents a single person.
/// </summary>
public class Person
{
public string Name;
public int Age;
public bool PreConditions;
}
}
using
System;
using
System.Data;
using
System.Web;
using
System.Collections;
using
System.Web.Services;
using
System.Web.Services.Protocols;
using
System.ComponentModel;
namespace
InsuranceWS
{
/// <summary>
///
Generates a quote for a insurance group rate.
/// </summary>
[
WebService
(Namespace =
"http://InsuranceWS.com/"
)]
public class
Quote
: System.Web.Services.
WebService
{
[
WebMethod
]
public decimal
GetQuote(
string
companyName,
Group
customers)
{
decimal
quote = customers.people.Length * 100;
return
quote;
}
}
/// <summary>
///
Represents the people in the group.
/// </summary>
public class
Group
{
public
Person
[] people;
}
/// <summary>
///
Represents a single person.
/// </summary>
public class
Person
{
public string
Name;
public int
Age;
public bool
PreConditions;
}
}
I know, I know, we are consuming an ASMX service, not WCF.
We still need to use the WCF service consuming wizard to
create a WCF adapter compatible schema. On the Metadata
Source screen, select Metadata Exchange (MEX) endpoint, and
then click Next.
On the Metadata Endpoint screen,
enter the URL of the service WSDL,
click Get, and then click Next.
You’re done! Here is your new
schema:
A Glimpse of the BizTalk Server 2006 R2 WCF Adapters 25
<?xml version="1.0"?>
<xs:schema xmlns:tns="http://InsuranceWS.com/"
elementFormDefault="qualified"
targetNamespace="http://InsuranceWS.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="GetQuote">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="companyName"
type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="customers"
type="tns:Group" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Group">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="people"
type="tns:ArrayOfPerson" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfPerson">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Person"
nillable="true" type="tns:Person" />
</xs:sequence>
</xs:complexType>
<?
xml
version
=
"
1.0
"
?>
<
xs:schema
xmlns:tns
=
"
http://InsuranceWS.com/
"
elementFormDefault
=
"
qualified
"
targetNamespace
=
"
http://InsuranceWS.com/
"
xmlns:xs
=
"
http://www.w3.org/2001/XMLSchema
"
>
<
xs:element
name
=
"
GetQuote
"
>
<
xs:complexType
>
<
xs:sequence
>
<
xs:element
minOccurs
=
"
0
"
maxOccurs
=
"
1
"
name
="
companyName
"
type
=
"
xs:string
"
/>
<
xs:element
minOccurs
=
"
0
"
maxOccurs
=
"
1
"
name
=
"
customers
"
type
=
"
tns:Group
"
/>
</
xs:sequence
>
</
xs:complexType
>
</
xs:element
>
<
xs:complexType
name
=
"
Group
"
>
<
xs:sequence
>
<
xs:element
minOccurs
=
"
0
"
maxOccurs
=
"
1
"
name
=
"
people
"
type
=
"
tns:ArrayOfPerson
"
/>
</
xs:sequence
>
</
xs:complexType
>
<
xs:complexType
name
=
"
ArrayOfPerson
"
>
<
xs:sequence
>
<
xs:element
minOccurs
=
"
0
"
maxOccurs
=
"
unbounded
"
name
=
"
Person
"
nillable
=
"
true
"
type
=
"
tns:Person
"
/>
</
xs:sequence
>
</
xs:complexType
>
26 A Glimpse of the BizTalk Server 2006 R2 Adapters
<xs:complexType name="Person">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="1" name="Age" type="xs:int" />
<xs:element minOccurs="1" maxOccurs="1" name="PreConditions"
type="xs:boolean" />
</xs:sequence>
</xs:complexType>
<xs:element name="GetQuoteResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="GetQuoteResult"
type="xs:decimal" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<
xs:complexType
name
=
"
Person
"
>
<
xs:sequence
>
<
xs:element
minOccurs
=
"
0
"
maxOccurs
=
"
1
"
name
=
"
Name
"
type
=
"
xs:string
"
/>
<
xs:element
minOccurs
=
"
1
"
maxOccurs
=
"
1
"
name
=
"
Age
"
type
=
"
xs:int
"
/>
<
xs:element
minOccurs
=
"
1
"
maxOccurs
=
"
1
"
name
=
"
PreConditions
"
type
=
"
xs:boolean
"
/>
</
xs:sequence
>
</
xs:complexType
>
<
xs:element
name
=
"
GetQuoteResponse
"
>
<
xs:complexType
>
<
xs:sequence
>
<
xs:element
minOccurs
=
"
1
"
maxOccurs
=
"
1
"
name
=
"
GetQuoteResult
"
type
=
"
xs:decimal
"
/>
</
xs:sequence
>
</
xs:complexType
>
</
xs:element
>
</
xs:schema
>
If you examine the schema, you can
see that there are a few general rules
to follow to build a schema that works
with the WCF adapter.
The schema targetNamespace 1.
must match the web service
targetNamespace.
The schema root node name must 2.
match the web method name.
The schema typically contains 3.
another root node named “<web
method name>+Response” to store
the web method response.
Most of the time you do not need to
know these rules because the wiz-
ard generates your schema for you.
However, sometimes you will want to
modify an existing schema by hand,
perhaps because of a service param-
eter change, or you may need to build
a schema by hand because the schema
generation wizard does not like a
particular WSDL in an interoperabil-
ity scenario. The schema generation
wizard also adds an orchestration and
two binding  les to your project. At
rst glance, the orchestration appears
empty, but if you open the orchestra-
tion view, you will notice that it con-
tains two multi-part message types.
The body parts of these message types contain the request and response types
from the schema you just generated. You use these message types as a wrapper
for sending the schema to the WCF send port. To connect with this web service,
you just need to add a request-response send port to the orchestration and con-
gure it to use these multi-part message types. When you deploy the orchestra-
tion you can import the generated binding  les to con gure the web service send
port. The wizard generates a WCF-BasicHTTP binding  le, which is a good choice
for consuming an ASMX service, and a WCF-Custom binding  le which is useful if
you need even  ner grained control over the communication.
One of my favorite WCF adapter features is the ability to harness the built in WCF
tracing ability. Tracing is extremely useful for troubleshooting external commu-
nications because it allows you to see the actual SOAP envelope that the adapter
transmitted as well as many other transmission details. To enable WCF tracing,
add the following XML to the BizTalk con guration  le (<BizTalk Install Directory>\
BTSNTSvc.exe.con g) directly above the closing <con guration/> tag, and restart
the host instance.
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="c:\logs\messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="false"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
<
system.diagnostics
>
<
sources
>
<
source
name
=
"
System.ServiceModel.MessageLogging
"
>
<
listeners
>
<
add
name
=
"
messages
"
type
=
"
System.Diagnostics.XmlWriterTraceListener
"
initializeData
=
"
c:\logs\messages.svclog
"
/>
</
listeners
>
</
source
>
</
sources
>
</
system.diagnostics
>
<
system.serviceModel
>
<
diagnostics
>
<
messageLogging
logEntireMessage
=
"
true
"
logMalformedMessages
=
"
true
"
logMessagesAtServiceLevel
=
"
false
"
logMessagesAtTransportLevel
=
"
true
"
maxMessagesToLog
=
"
3000
"
maxSizeOfMessageToLog
=
"
2000
"
/>
</
diagnostics
>
</
system.serviceModel
>
A Glimpse of the BizTalk Server 2006 R2 Adapters 27
The initializeData  eld speci es the  le to which WCF will write trace data. You can
view the trace  le using the Microsoft Service Trace Viewer tool:
This is worth the cost of the product by itself!
Conclusion
The new WCF adapters provide impressive  exibility in interoperability scenarios as
well as performance in BizTalk to WCF scenarios. If you haven’t implemented many
of your services in WCF, don’t worry, you can still use the WCF-BasicHttp or WCF-
WSHttp adapter and take advantage of the rich WCF functionality such as tracing.
When you do want to upgrade those services to WCF, you can turbo-charge your
BizTalk to service communications as easily as selecting the WCF-NetTcp adapter.
Hopefully this article has given you insight into selecting the right adapter for your
solution and showed you how to go about using a WCF adapter to consume a
service.
Introduction
BizTalk Maps have their place in many projects, and can offer signifi cant value in the right scenario. How-
ever, it is occasionally better to consider orienting your project towards a “straight XSLT
approach for your mapping needs, bypassing the mapper altogether. This article dis-
cusses the when/why/how of using XSLT within your BizTalk projects, including
a few helpful source snippets.
Note that even though BizTalk maps do indeed leverage XSLT inter-
nally, when we use the term “XSLT” in this article were talking
about authoring XSLT directly and bypassing the mapper.
The primary scenarios where using “straight XSLT” can be
of benefi t instead of a traditional BizTalk Map are sum-
marized below.
If your mapping is easily expressed by the
“shape” of the destination schema, the XSLT
approach can be a bit more natural, since
examining the XSLT in source form will
often quickly show you how the des-
tination content is populated by the
source. Moreover, as the number of
links grows larger in a traditional
map, you will fi nd that maintaining
XSLT can become the more trac-
table option – providing you have
XSLT skills on hand.
When multiple developers are
involved in map development
and/or you need to support mul-
tiple code lines with branching
and merging, you will fi nd that the
ability to do three-way merging
with XSLT is straightforward, where
it is problematic with a BizTalk map.
In a development cycle that is heav-
ily driven by rapidly changing trans-
forms, it can be a huge productivity
gain to simply update a fi le on disk
(without having to “re-gac” or redeploy
an assembly.) This is particularly true in
development or QA environments that
are one step removed from a developer
workstation. Proper use of XSLT enables this
scenario.
In performance-sensitive situations, realize that
BizTalk uses the .NET 1.1 XslTransform class when
it executes maps, instead of the .NET 2.0 XslCom-
piledTransform class. With a little bit of work, you can use
XslCompiledTransform with your XSLT and realize signifi cant
performance gains. (In addition, you can decide to use a third
party transform library that offers XSLT 2.0 support.)
Scott Colestock
Trace Ventures, LLC
Smart Use of XSLT BizTalk 29
Of course, many projects take advantage of the “Custom
XSLT Path” option available to you in the property pane of
a BizTalk map (see Figure 1). This can give you the  rst two
bene ts above, but not the opportunity for the last two.
Figure 1
Note: One instance where using the BizTalk mapper can
be useful is for very large messages that require the use of
BizTalk’s disk-backed transform model. Though it may be
possible to call the internal BTSXslTransform class from code
(passing your own XSLT), that technique is not discussed in
this article. Of course, other instances where the mapper can
provide value are those cases where functoids provide func-
tionality not easily reproduced in XSLT.
With that said, if you’re convinced that using a “straight XSLT
approach is the way to go for your particular project, you will
nd that there is some core component that will be useful to
you.
Executing Your XSLT
To get the bene ts de ned above, we’d like to be able to use
the MSIL-generating XslCompiledTransform class, but cache
our compilations for performance. In addition, we’d like to
expire our cache if the underlying XSLT  le changes (to sup-
port on-the- y updates). To accomplish this, we’re going to
use the Microsoft Enterprise Library (http://www.codeplex.
com/entlib). Note that we can use just the portion of the
library that we need – the functionality is well factored. If we
have a class library project in our solution, we’ll simply add
references to the “Microsoft.Practices” assemblies shown in
Figure 2.
Figure 2
With this in mind, what might an “ApplyTransform” method
look like? Below is an implementation you can leverage.
The containing class initializes the Enterprise Library fea-
tures we are using in a static constructor, and uses a static
instance of the Enterprise Library “CacheManager” class. The
ApplyTransform method itself takes in the document to be
transformed as an XmlDocument. (Remember that BizTalk
messages can be passed to such methods without casting,
etc.) It also takes in the name of the XSLT  le itself, though
without path quali cation. This is to simplify the job of the
caller, which will typically be an orchestration—though you
could obviously write a pipeline component to be a caller as
well. (In the latter case, give thought to whether you instead
want an implementation that preserves the streaming model
within the pipeline.)
An orchestration—within a “Message Assignment” shape
can simply issue a call like this:
sampleOut = BizTalkXsltDemo.Components.XmlUtility.ApplyTransform(
sampleIn,"Sample.xsl");
sampleOut = BizTalkXsltDemo.Components.XmlUtility.ApplyTransform(
sampleIn,"Sample.xsl");
The ApplyTransform method does a thread-safe check to see
if a compiled representation of the requested XSLT is already
cached. If it is not, it takes care of preparing it and caching it
with a “cache dependency” on the underlying XSLT  le. We
also take some extra steps to allow for xsl:strip-space opera-
tions, use of the XSL document() function, use of embedded
script blocks, and use of xslt imports and includes.
30 Smart Use of XSLT BizTalk
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Xsl;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
public class XmlUtility
{
// Our static cache manager for managing cached items.
static CacheManager _cache;
// Disk location - we expect to find our Enterprise Library configuration and
// XSL files relative to this path.
static string _workingPath = null;
static XmlUtility()
{
// We need to query the SSO or some other configuration store to determine where our
// application is installed (on a server) or "lives" on a developer workstation.
//
// Replace this line with whatever configuration mechanism you use.
//
_workingPath = SSOSettingsFileReader.ReadString("BizTalkXsltDemo", "WorkingPath");
FileConfigurationSource fileSource = new FileConfigurationSource(
Path.Combine(_workingPath, "EntLib.config"));
CacheManagerFactory factory = new CacheManagerFactory(fileSource);
_cache = factory.CreateDefault();
}
/// <summary>
/// Applies an xsl transform and returns the result.
/// </summary>
/// <param name="toTransform">An XmlDocument instance that should act as the transform
source.</param>
/// <param name="xslFileName">A bare file name (no path) for the XSLT. Must live in the
"Xslt" subdirectory.</param>
/// <returns></returns>
public static XmlDocument ApplyTransform(
XmlDocument toTransform,
string xslFileName)
{
// Attempt retrieval of compiled xslt from the cache.
XslCompiledTransform xslt = null;
xslt = (XslCompiledTransform)_cache.GetData(xslFileName);
if (xslt == null)
{
lock (_cache)
{
xslt = (XslCompiledTransform)_cache.GetData(xslFileName);
if (xslt == null)
{
// If not cached, load it up and cache it.
string fullSpec = Path.Combine(Path.Combine(_workingPath,"xslt"), xslFileName);
XmlDocument styleSheet = new XmlDocument();
styleSheet.Load(fullSpec);
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.IO;
using
System.Xml;
using
System.Xml.Xsl;
using
Microsoft.Practices.EnterpriseLibrary.Caching;
using
Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
using
Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
public
class
XmlUtility
{
// Our static cache manager for managing cached items.
static
CacheManager
_cache;
// Disk location - we expect to find our Enterprise Library configuration and
// XSL files relative to this path.
static
string
_workingPath =
null
;
static
XmlUtility()
{
// We need to query the SSO or some other configuration store to determine where our
// application is installed (on a server) or "lives" on a developer workstation.
//
// Replace this line with whatever configuration mechanism you use.
//
_workingPath =
SSOSettingsFileReader
.ReadString(
"BizTalkXsltDemo"
,
"WorkingPath"
);
FileConfigurationSource
fileSource =
new
FileConfigurationSource
(
Path
.Combine(_workingPath,
"EntLib.config"
));
CacheManagerFactory
factory =
new
CacheManagerFactory
(fileSource);
_cache = factory.CreateDefault();
}
///
<summary>
///
Applies an xsl transform and returns the result.
///
</summary>
///
<param name="toTransform">
An XmlDocument instance that should act as the transform
source.
</param>
///
<param name="xslFileName">
A bare file name (no path) for the XSLT. Must live in the
"Xslt" subdirectory.
</param>
///
<returns></returns>
public
static
XmlDocument
ApplyTransform(
XmlDocument
toTransform,
string
xslFileName)
{
// Attempt retrieval of compiled xslt from the cache.
XslCompiledTransform
xslt =
null
;
xslt = (
XslCompiledTransform
)_cache.GetData(xslFileName);
if
(xslt ==
null
)
{
lock
(_cache)
{
xslt = (
XslCompiledTransform
)_cache.GetData(xslFileName);
if
(xslt ==
null
)
{
// If not cached, load it up and cache it.
string
fullSpec =
Path
.Combine(
Path
.Combine(_workingPath,
"xslt"
), xslFileName);
XmlDocument
styleSheet =
new
XmlDocument
();
styleSheet.Load(fullSpec);
To get a sense for what performance
can look like using compiled XSLT, see
Daniel Probert’s excellent performance
benchmarks at http://shurl.org/map-
per f (or search for “mapper perfor-
mance and maintainability).
Calling Code From Your
XSLT
You may  nd that it is desirable to
call C# (or VB.NET) code to deal with
a task that is not well suited for XSLT.
For those new to XSLT, you have two
options in this case. The  rst is to sim-
ply embed a script block in your XSLT in
the traditional way:
Smart Use of XSLT BizTalk 31
// Construct an XsltSettings to allow for the Xsl document() function
// as well as for embedded script blocks.
XsltSettings settings = new XsltSettings(true, true);
// Specify XmlUrlResolver() to enable Xslt import and include elements.
xslt = new XslCompiledTransform();
xslt.Load(styleSheet, settings, new XmlUrlResolver());
// Cache the compiled xslt, ensuring cache flush via FileDependency
// if underlying xslt file changes.
_cache.Add(
xslFileName,
xslt,
CacheItemPriority.Normal,
null,
new FileDependency(fullSpec));
}
}
}
// We want to supply our input to the transform as a node reader to allow for
// Xslt operations like xsl:strip-space (which require a streaming input)
XmlReader readerToTransform = new XmlNodeReader(toTransform);
XmlDocument transformedDoc = new XmlDocument();
using (XmlWriter writer = transformedDoc.CreateNavigator().AppendChild())
{
xslt.Transform(readerToTransform, (XsltArgumentList)null, writer);
}
return transformedDoc;
}
}
// Construct an XsltSettings to allow for the Xsl document() function
// as well as for embedded script blocks.
XsltSettings
settings =
new
XsltSettings
(
true
,
true
);
// Specify XmlUrlResolver() to enable Xslt import and include elements.
xslt =
new
XslCompiledTransform
();
xslt.Load(styleSheet, settings,
new
XmlUrlResolver
());
// Cache the compiled xslt, ensuring cache flush via FileDependency
// if underlying xslt file changes.
_cache.Add(
xslFileName,
xslt,
CacheItemPriority
.Normal,
null
,
new
FileDependency
(fullSpec));
}
}
}
// We want to supply our input to the transform as a node reader to allow for
// Xslt operations like xsl:strip-space (which require a streaming input)
XmlReader
readerToTransform =
new
XmlNodeReader
(toTransform);
XmlDocument
transformedDoc =
new
XmlDocument
();
using
(
XmlWriter
writer = transformedDoc.CreateNavigator().AppendChild())
{
xslt.Transform(readerToTransform, (
XsltArgumentList
)
null
, writer);
}
return
transformedDoc;
}
}
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:ext="urn:my-scripts"
exclude-result-prefixes="ext msxsl">
<xsl:template match="/">
<MySampleOutput>
<Date>
<xsl:value-of select="ext:Today()"/>
</Date>
<MyDestinationData>
<xsl:value-of select="MySourceData" />
</MyDestinationData>
</MySampleOutput>
</xsl:template>
<msxsl:script implements-prefix="ext" language="CSharp">
public string Today()
{
return DateTime.Now.ToString();
}
</msxsl:script>
</xsl:stylesheet>
<?
xml
version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<
xsl:stylesheet
version
=
"
1.0
"
xmlns:xsl
=
"
http://www.w3.org/1999/XSL/Transform
"
xmlns:msxsl
=
"
urn:schemas-microsoft-com:xslt
"
xmlns:ext
=
"
urn:my-scripts
"
exclude-result-prefixes
=
"
ext msxsl
"
>
<
xsl:template
match
=
"
/
"
>
<
MySampleOutput
>
<
Date
>
<
xsl:value-of
select
=
"
ext:Today()
"
/>
</
Date
>
<
MyDestinationData
>
<
xsl:value-of
select
=
"
MySourceData
"
/>
</
MyDestinationData
>
</
MySampleOutput
>
</
xsl:template
>
<
msxsl:script
implements-prefix
=
"
ext
"
language
=
"
CSharp
"
>
public string Today()
{
return DateTime.Now.ToString();
}
</
msxsl:script
>
</
xsl:stylesheet
>
The second is to call an external assembly (still from an xslt script block), which requires
a fully-quali ed assembly reference since (with a BizTalk application) your assembly is
likely deployed to the global assembly cache. This looks as follows:
32 Smart Use of XSLT BizTalk
<msxsl:script implements-prefix="ext" language="CSharp">
<msxsl:assembly name="BizTalkXsltDemo.Components, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=9e94a5886867ebdc" />
<msxsl:using namespace="BizTalkXsltDemo.Components" />
public string WrapAddFiveDays(string date)
{
// This class and method is in our components assembly.
return XsltScriptHelper.AddFiveDays(date);
}
</msxsl:script>
<
msxsl:script
implements-prefix
=
"
ext
"
language
=
"
CSharp
"
>
<
msxsl:assembly
name
=
"
BizTalkXsltDemo.Components, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=9e94a5886867ebdc
"
/>
<
msxsl:using
namespace
=
"
BizTalkXsltDemo.Components
"
/>
public string WrapAddFiveDays(string date)
{
// This class and method is in our components assembly.
return XsltScriptHelper.AddFiveDays(date);
}
</
msxsl:script
>
You can always use xsl:include to put all of your script in an
external XSLT  le to be reused by multiple XSLTs. (That is
an additional bene t of XslCompiledTransform, by the way
– XSLT includes and imports are supported.) Also, instead
of the approaches above, you could elect to use true XSLT
extension objects, which can provide a more natural-looking
xslt syntax. However, these have to be “presented” to the
XsltCompiledTransform instance (via XsltArgumentList) at the
point of execution, which limits  exibility a bit.
Creating a “Composite” Input
For Your XSLT
One of the features people have liked about the mapper is the
ability to specify multiple inputs to a map. Behind the scenes,
the mapper is simply creating a “wrapper” around the partici-
pating input schemas. At runtime, your map inputs are simply
provided to the XSLT underneath a common root element.
In order to get a similar effect with the straight XSLT
approach we’re discussing here, you can rely on a helper
function that looks as follows. This method simply takes an
arbitrary number of input documents and places them under
a common root. Now, the XSLT that you author can be writ-
ten to expect multiple inputs.
/// <summary>
/// Used to create a composite document, rooted with a root element named "root",
from several
/// input documents.
/// </summary>
/// <param name="docs">Variable length argument list of XmlDocuments.</param>
/// <returns></returns>
public static XmlDocument CreateCompositeDocument(
params XmlDocument[] docs)
{
XmlDocument returnDoc = new XmlDocument();
returnDoc.LoadXml("<root/>");
for (int i = 0; i < docs.GetLength(0); i++)
{
if (docs[i] != null)
{
XmlNode node = docs[i].FirstChild;
// Get past leading PI, comments, etc.
while (node.NodeType != XmlNodeType.Element)
node = node.NextSibling;
returnDoc.FirstChild.AppendChild(returnDoc.ImportNode(node, true));
}
}
return returnDoc;
}
///
<summary>
///
Used to create a composite document, rooted with a root element named "root",
from several
///
input documents.
///
</summary>
///
<param name="docs">
Variable length argument list of XmlDocuments.
</param>
///
<returns></returns>
public
static
XmlDocument
CreateCompositeDocument(
params
XmlDocument
[] docs)
{
XmlDocument
returnDoc =
new
XmlDocument
();
returnDoc.LoadXml(
"<root/>"
);
for
(
int
i = 0; i < docs.GetLength(0); i++)
{
if
(docs[i] !=
null
)
{
XmlNode
node = docs[i].FirstChild;
// Get past leading PI, comments, etc.
while
(node.NodeType !=
XmlNodeType
.Element)
node = node.NextSibling;
returnDoc.FirstChild.AppendChild(returnDoc.ImportNode(node,
true
));
}
}
return
returnDoc;
}
Remember the XSLT Debugger
Say…remember that Visual Studio 2005 and above has a full
XSLT debugger with single-step support and the ability to
see your output being assembled on the  y. In addition, the
intellisense provided in the IDE while authoring XSLT is quite
helpful. That said, you may want to look into the various 3rd
party tools that specialize in XSLT development.
Calling Other XSLT Processors
XSLT 2.0 offers a lot of features that can make XSLT work a
lot easier – see ht tp://www.w3.org/TR/x path-f unc tions to
get a sense of what you have for dealing with strings, math,
dates, times, etc. under XSLT 2.0. However, it isn’t supported
in the current System.Xml libraries. Never fear, you can take
a look at third party libraries, such as the open source Sax-
onB implementation. See http://sourceforge.net/projects/
saxon. (This library supports the “EXSLT” extension functions
as well.) We can extend the concepts we’ve been talking
about to call the SaxonB transforms with the same “cache our
compilations” strategy:
Smart Use of XSLT BizTalk 33
An orchestration that is calling this code will typically do something like the following:
// Create the composite document that the transform expects.
xmlDoc = BizTalkXsltDemo.Components.XmlUtility.CreateCompositeDocument(
someInput1,someInput2,someInput3);
resultMsg = BizTalkXsltDemo.Components.XmlUtility.ApplyTransform (
xmlDoc,@"myTransform.xsl");
// Create the composite document that the transform expects.
xmlDoc = BizTalkXsltDemo.Components.XmlUtility.CreateCompositeDocument(
someInput1,someInput2,someInput3);
resultMsg = BizTalkXsltDemo.Components.XmlUtility.ApplyTransform (
xmlDoc,@"myTransform.xsl");
/// <summary>
/// Applies an xsl transform using the SaxonB library and returns the result.
/// Enables use of XSLT 2.0 and Saxon’s extension library, among other things.
/// </summary>
/// <param name="toTransform">An XmlDocument instance that should act as the transform
source.</param>
/// <param name="xslFileName">A bare file name (no path) for the XSLT. Must live in the
"Xslt" subdirectory.</param>
/// <returns></returns>
public static XmlDocument ApplyTransformWithSaxonB(
XmlDocument toTransform,
string xslFileName)
{
string cacheKey = xslFileName + "sax";
// Attempt retrieval of compiled xslt from the cache. Appending "sax" to avoid
// key collision in the rare case where same stylesheet is used with ApplyTransform.
XsltTransformer xslt = null;
xslt = (XsltTransformer)_cache.GetData(cacheKey);
if (xslt == null)
{
lock (_cache)
{
xslt = (XsltTransformer)_cache.GetData(cacheKey);
if (xslt == null)
{
// If not cached, load it up and cache it.
string fullSpec = Path.Combine(Path.Combine(_workingPath, "xslt"), xslFileName);
XmlDocument styleSheet = new XmlDocument();
styleSheet.Load(fullSpec);
// Create a compiler
XsltCompiler compiler = _processor.NewXsltCompiler();
// Compile the stylesheet
xslt = compiler.Compile(new XmlNodeReader(styleSheet)).Load();
///
<summary>
///
Applies an xsl transform using the SaxonB library and returns the result.
///
Enables use of XSLT 2.0 and Saxon’s extension library, among other things.
///
</summary>
///
<param name="toTransform">
An XmlDocument instance that should act as the transform
source.
</param>
///
<param name="xslFileName">
A bare file name (no path) for the XSLT. Must live in the
"Xslt" subdirectory.
</param>
///
<returns></returns>
public
static
XmlDocument
ApplyTransformWithSaxonB(
XmlDocument
toTransform,
string
xslFileName)
{
string
cacheKey = xslFileName +
"sax"
;
// Attempt retrieval of compiled xslt from the cache. Appending "sax" to avoid
// key collision in the rare case where same stylesheet is used with ApplyTransform.
XsltTransformer
xslt =
null
;
xslt = (
XsltTransformer
)_cache.GetData(cacheKey);
if
(xslt ==
null
)
{
lock
(_cache)
{
xslt = (
XsltTransformer
)_cache.GetData(cacheKey);
if
(xslt ==
null
)
{
// If not cached, load it up and cache it.
string
fullSpec =
Path
.Combine(
Path
.Combine(_workingPath,
"xslt"
), xslFileName);
XmlDocument
styleSheet =
new
XmlDocument
();
styleSheet.Load(fullSpec);
// Create a compiler
XsltCompiler
compiler = _processor.NewXsltCompiler();
// Compile the stylesheet
xslt = compiler.Compile(
new
XmlNodeReader
(styleSheet)).Load();
34 Smart Use of XSLT BizTalk
// Cache the compiled xslt, ensuring cache flush via FileDependency
// if underlying xslt file changes.
_cache.Add(
cacheKey,
xslt,
CacheItemPriority.Normal,
null,
new FileDependency(fullSpec));
}
}
}
// Run the compiled transformation.
XdmNode input = _processor.NewDocumentBuilder().Build(toTransform.DocumentElement);
xslt.InitialContextNode = input;
DomDestination result = new DomDestination();
xslt.Run(result);
return result.XmlDocument;
}
// Cache the compiled xslt, ensuring cache flush via FileDependency
// if underlying xslt file changes.
_cache.Add(
cacheKey,
xslt,
CacheItemPriority
.Normal,
null
,
new
FileDependency
(fullSpec));
}
}
}
// Run the compiled transformation.
XdmNode
input = _processor.NewDocumentBuilder().Build(toTransform.DocumentElement);
xslt.InitialContextNode = input;
DomDestination
result =
new
DomDestination
();
xslt.Run(result);
return
result.XmlDocument;
}
Summary
Using XSLT can provide a large productivity gain in many Biz-
Talk projects, given the ability to support source-level branch/
merge operations and the ability to update XSLT on a shared
server without redeployments or “re-gac” operations. Taking
control of the XSLT execution itself can provide run-time per-
formance bene ts and  exibility as to which XSLT implemen-
tation you use. With a little bit of utility code, you can quickly
be on your way.
You should consider this approach when any of the following
are true:
You have XSLT expertise on hand that you would like to
leverage within your project
You’re looking for additional performance out of your data
transformations, and/or XSLT 2.0 features
You need to support source-level branch/merge opera-
tions for data transformations
You’re looking for a more ef cient deployment model for
your data transformations (perhaps because they change
more frequently than the rest of the solution)
You should probably not consider this approach if:
You don’t have XSLT skills on the team
You are dealing with very large messages that would ben-
e t from BizTalk’s streaming transformation model
You want to support side-by-side versioning of maps using
multiple .NET assembly versions (via the global assembly
cache)
A complete code sample that includes all code discussed as
well as a consuming orchestration can be downloaded from
http://www.codeple x.com/biztal kxslt.
Running Header 35
As part of a new series, we will be exploring BizTalk from the
perspective of someone new to the product. In my opinion,
this is a very important aspect to explore. The reasoning is
somewhat obvious. While BizTalk can do everything aside
from the dishes, and don’t worry we are working on that,
this creates a fundamental problem for most new adoptions.
How do we stand this thing up? What problem are we trying
to solve? Are we building for today or the future? What are
the rami cations of building for today vs tomorrow? Who is
going to manage this thing? And the list goes on and on.
My goal in this series is to explore the product at a basic
level and address some of the questions and basic pitfalls we
see in the  eld when we are working with our customers.
So, where do we begin? One of the  rst questions I ask my
customers… what problem are you trying to solve? And for
the most part, they understand where BizTalk  ts into the
mix. We are trying to connect A to B. And to be honest,
that’s probably a great
starting point. If
your organiza-
tion is new
to Biz-
Talk, this is a great icebreaker for the team. What do I mean?
Let’s think about this for a second. This isn’t a new version of
Visual Studio or SQL Server. Therefore, there are infrastruc-
ture considerations that come into play. Now, I don’t want
to scare people off. It just needs to be clear – someone will
need to manage this new piece of infrastructure you are
about to introduce into the environment. And as much as
I love developers… let’s call a spade a spade… developers
should not be responsible for the management and scale of
the system. And most developers I know… don’t want to.
Back to my point. Here we sit, day 1 with a customer. They
have a problem and they aren’t quite sure if BizTalk will solve
the problem or where to begin with it. So, let’s talk about the
problems that BizTalk can solve. Now, this isn’t an exhaustive
list and I am constantly amazed at the way people are using
it – so feel free to let your creative juices  ow!
Here is a list of topics that usually start off every meeting
with a customer. So, let’s explore these at a 50,000ft level.
Integration.1.
Ah yes… this is where it all begins! Bottom line, almost
every organization is connecting systems today.
One way or another, this is happening. Could
be hard coded. Could be DTS/SSIS. Could
be manual. This is basic work for Biz-
Talk. And a GREAT starting point
Sal Cincotta
Principal Technology Specialist
BPM/SOA
36 In the beginning...
for any person or organization looking to make a business
case for using the tool.
Whether you are connecting enterprise applications or
working in a B2B scenario — BizTalk, truly is the right tool
for the job and it will make your life easier in the long
run. I know what you are thinking Mr Developer, Mr .NET
Genius, Mr I Code With The Lights Off… I don’t need no
stink’n BizTalk! Well my friend… you are wrong! Let me tell
you why.
No matter how well you code — changes are a neces-
sary evil of what we all do as developers. They come with
the territory. So, let me ask you this, who will own the
changes in your compiled code when you leave? How
many connection points do you have to manage? What
happens when one of the receiving systems or source
systems is ofine? How will your code scale? What hap-
pens when a new system comes on line? Every connection
point has to be touched??? IT Managers are you scared
yet?
I have a lot of side discussions with developers who see
BizTalk as a threat to what they do. Guys, and ladies, wake
up! This is not an either/or proposition. This is yet another
tool in your arsenal. It’s a way of loosely connecting your
systems so you don’t create a house of cards. It allows you
to easily manage and scale your systems following estab-
lished best practices.
Bottom line. BizTalk allows you to connect dozens of
systems right out of the box. From mainframe systems to
SAP and PeopleSoft, this is a great starting point for orga-
nizations looking at taking a rst step with the product.
For a list of adapters, visit the ofcial site www.microsoft.
com/biztalk
EDI/AS2.2.
Somewhat new to the product suite, EDI is something
we see a lot of companies using and something they
are spending a small fortune on at a transaction level
and something costing another fortune to maintain.
Cobol? Yuck! If you are a Cobol programmer. One word…
RETOOL! (I fully expect my inbox to ll up on this one. )
I don’t want to get into all the specics of EDI/AS2. Know
that it is supported and know that BizTalk will greatly
simplify your world in this area. We see a lot of customers
jumping on this feature to retire some older systems and
use this as an opportunity to retool their teams on current
technologies.
SOA.3.
SOA! The new buzz! Let’s do it shall we? NO! Stop! We see
so many companies doing this for the sake of doing it and
guess what? You are almost guaranteed to fail! Oh, and
if you talk to some of our competitors… they would have
you believe that SOA is something you can buy off the
shelf. Um… yes, I will take the grilled sprouts with a side of
SOA. No, it doesn’t work that way and don’t let manage-
ment fall for the hype. There is usually a multi-million dol-
lar price tag tied to it.
Now, I don’t mean to come across as a SOA hater. BUT
we need to think before we act when it comes to SOA.
What business problem are we trying to solve? Where
we see SOA initiatives fail is where IT takes a “build it and
they will come” mentality.
Microsoft’s position on SOA is that it should be a middle/
out approach. Meaning, align with business drivers and
strategic vision to build an iterative and owing system.
By doing this, you guarantee that your work is timely and
tied to the needs of the business. A 1 to 2 year project will
certainly not be timely and more than likely miss the boat
all together. In 2 years your requirements will be com-
pletely different. IT will constantly be playing catch-up.
By focusing on the business value and aligning with a real
business initiative, you can ensure that you are delivering
timely results that can be built upon with each new itera-
tion. This is what we have termed — “real world SOA.”
In addition, there are all different levels of SOA maturity
that an organization will go through – which are beyond
the scope of this article. However, this is something to
be mindful of because as you follow our approach and
continue to build upon your SOA infrastructure – you
will need governance tools geared specically towards
managing the SOA infrastructure. Tools like those offered
from AmberPoint and SOA Software.
Here is a SOA overview | http://www.microsoft.com/biz-
talk/solutions/soa/overview.mspx
ESB.4.
Aka Enterprise Service Bus. As you continue to evolve
your SOA infrastructure you will undoubtedly start think-
ing about an ESB.
And let’s just start the fun shall we? There is constant dis-
agreement among people on the true meaning of an ESB.
Is it an architectural style, a software product, a suite of
products? Ask the question and your head is sure to spin
with the multitude of answers. In my opinion, it is a group
of products that enable the infrastructure. Now, my goal
is not to fuel this debate or inject myself into the center of
one. However, let’s explore this.
An ESB will ideally reduce the number of point to point
contacts thus reducing the impact of any software
changes to the environment. All communication should
take place via the bus. By reducing the number of direct
contacts the process of adapting your systems to change
becomes that much easier. And ultimately, isn’t that the
In the beginning... 37
goal? We come back to the concept of a loosely coupled
infrastructure. At the center of this… BizTalk.
My recommendation would be to take a look at our ESB
Guidance document and ensure you understand, at least
at a high level, what it is and how it should work.
http://www.codeple x.com/esb
RFID.5.
New to the R2 release is support for RFID. Soon, my now
ancé, will be able to track what isle I am in at the gro-
cery store and make sure I don’t miss anything that I am
supposed to pick up. All run through our central BizTalk
server back at the house. Lucky for me BizTalk is Greek to
her.
On a serious note, this is some serious technology and
Microsoft is backing this to bring it to the mainstream. I
don’t want to explore all the details of what RFID is and
isn’t. If you don’t know what RFID is – I suggest that you
spend about 5 min and look it up on Live Search. If your
organization is not using it today, you will soon be hit
with a plethora of ideas on where it can be used to save
the organization some time and money. And talk about
access to information!
Obviously, BizTalk is one piece to the equation here. You
will need tags and readers, among other things. BizTalk
creates the infrastructure to enable a plug and play
environment for your devices. It is designed to provide
a scalable, extensible platform for the development and
management of rich RFID and sensor solutions.
We have a large community of partners in the eco-system
all bringing their own expertise to the game.
Check out this link for more information | http://www.
microsoft.com/biztalk/technologies/rd/default.mspx
Now with all this being said, clearly, we did not touch on
everything there is to touch on. Your take away should
be… “dam, this thing can do a lot! I need to learn more.”
Why is that important? Because once you adopt BizTalk it
grows like a weed in the organization. Why? We nd that
once an organization adopts the technology, even for a
small project, they then become comfortable with it and
realize that this is a tool. And like any other tool, it can be
used for multiple purposes.
BizTalk is one of those applications that the cost is
absorbed up front. Meaning, once you correctly imple-
ment the architecture, adding new applications to it
becomes somewhat trivial. The pain is upfront — this is
what allows it to grow like wildre. It is also what leads
to so many failed installations. Don’t underestimate the
importance of planning.
For those of you new to the product… welcome! I hope
this has given you a brief glimpse of what your future
will hold! Educate yourself on the product — knowledge
is truly power and I don’t know any unemployed BizTalk
professionals. They are all in high demand! Now that, my
friends… is job security.
Sal Cincotta
scincott@microsoft.com
38 Running Header
This document describes the use of the BizTalk Server 2006
extensions for WF.
Introduction
I haven’t driven a race car, but I used to skydive. On a regu-
lar basis, people would ask me why I would jump out of a
perfectly good airplane. The common response for most
skydivers was, “There’s no such thing as a perfectly good
airplane” (Seriously, those things were held together by duct
tape). When I heard about the BizTalk Server 2006 Exten-
sions for Windows Workow Foundation SDK, I had a similar
reaction. “Why would I want to host a windows workow
process in BizTalk when I have a perfectly good orchestration
engine?” These days, I don’t skydive anymore, so I gured Id
live as dangerous as I get now and give this toolset a chance.
Most people are pretty familiar with Windows Workow
Foundation, but it seems to be slightly misunderstood. At
rst glance, people usually think it is a toolset to build human
based workow solutions. On MSDN, it’s described as:
Windows Workow Foundation is the programming model,
engine and tools for quickly building workow enabled appli-
cations on Windows. It consists of a .NET Framework version
3.0 (formerly WinFX) namespace, an in-process workow
engine, and designers for Visual Studio 2005.
Programming model, engine, and tools...sounds like a lot of
work. So, I can build an application that hosts these “work-
ows” myself (or I can just purchase MOSS which hosts, WF
and is really good at human workow). Well, I’m a BizTalk
Architect and I have my own “business process” tools, so I’ll
just stick with those. But hey, orchestrations are a lot like
workow… I wonder if BizTalk is going away? Well, luckily for
all of us, it’s not. In fact, from everything I’ve heard, the new
version of BizTalk Server (codenamed Oslo) will host both the
XLANG orchestrations we are used to today and WF based
workows. I know we all like nice shiny tools, so I’m sure
you’re just as excited as me to get started and take a look
under the hood to see how BizTalk and WF can soup up our
workow engine.
Chris Kabat
What’s Under That Hood? 39
Windows Workflow Foundation
WF provides the capability to build visual representations
of business logic or process in Visual Studio. There are two
ways this can be done:
Sequential Work ows (think BizTalk Orchestration 1.
designer) for sequential processing
State Machine Work ows (doesn’t that take you back to 2.
computer science class) for more event driven processing
WF also provides its own business rule engine and provides
the ability to create reusable activities. The business rules
engine is very straightforward, but nowhere near as powerful
as BizTalk’s enterprise rules engine.
An important part of any WF solution is the need for a plat-
form or application to host the work ow. WF does not really
provide an enterprise ready host out of the box, it requires
you build an application in .NET to host the work ow. That’s
all well and good if it’s going to run on someone’s desktop,
maybe just doing a little business logic, but what if these
work ows need to be hosted on a highly scalable and high
performance platform. Since it is just .NET, couldn’t we host
our work ow in an orchestration? Yes, we can, but it would
be a ton of custom code every time we wanted to host a WF
work ow. Luckily, Jon Flanders and Paul Andrew did this
work for us and created the BizTalk Server 2006 extensions
for Windows Work ow Foundation SDK.
The Biztalk Server 2006 Extensions for
Windows Workflow Foundation SDK
The BizTalk Server 2006 extensions for Windows Work ow
Foundation SDK (known here on out as “the Extensions SDK)
is a toolkit that allows us to quickly host a WF work ow in
BizTalk. Basically, it’s a set of tools that live within Visual Stu-
dio that help a developer to host a WF work ow in BizTalk
without creating the shims or custom code manually. At a
high level, this is how it works:
The developer creates a sequential work ow using a tem-
plate that is installed with the Extensions SDK.
Custom activities are used to receive and send messages
in and out of the work ow. WCF Opera-
tionContracts and Data/Message Contracts
are used to de ne messages passed in or
out. The activities can support a one-way or
request-response pattern.
Once the work ow project is built, strong
named, and put in the GAC, there is an option
to “Generate Orchestration”. This option
builds an orchestration to host the work ow.
If using complex patterns, the developer can
then modify the BizTalk Solution if needed
(often correlation is added to allow long running pro-
cesses to be implemented).
This BizTalk solution can now be deployed, and con g-
ured. Ports created by the extension SDK can be bound
to WCF ports.
It’s pretty simple to get a basic pattern working in a short
period of time. With the Extensions SDK, there is a Quick-
Start document that will walk through an example step-
by-step (actually 39 steps). This document is invaluable for
getting started quickly.
There are some limitations we should be aware of. First,
state machine work ows are not supported. The docs say
that the model doesn’t seem to  t well with in BizTalk. This
was pretty disappointing to me, and I really hope this is
covered in Oslo. There are also a few other features not sup-
ported such as ConditionalActivityGroups. Also, complex,
convoy type patterns are pretty hard to implement as well
(But that is what BizTalk is for, right?).
On the  ipside, complex business logic can be tough to imple-
ment in BizTalk and I see that as a huge plus for the extensions
SDK. Working in normal Visual Studio code windows instead
of the tiny expression window is also nice. Debugging can also
be done in Visual Studio too in a very seamless way.
Let’s walk through a basic example and take the Extensions
SDK for a test drive.
The Expense Approval Process
To take a walk through the Extensions SDK, I created a very
simple expense approval process. Basically, it does the
following:
Accepts an Employee Name, and amounts for meals,
lodging, etc.
Executes some WF based business rules to  ag the report
for large expenditures, etc.
Allows for management approval.
To facilitate this process, we built a very small test harness to
drive the process:
In the example depicted above, I
entered a very large miscellaneous
expense. As you can see, the report
was denied because a miscellaneous
amount of more than $1000 was
entered. This was validated by pro-
cessing a WF based set of business
rules. To implement the work ow that
processes the expense report, I did the
following:
Installed the BizTalk Server 2006 1.
extensions for Windows Work ow
SDK and installed/imported the Biz-
TalkArtifacts.msi.
Created a new project named 2.
ExpenseWork ow using the BizTalk-
WFTemplate template provided by
the Extensions SDK.
De ned my Service Contract, 3.
Operation Contracts, and DataCon-
tract. The SubmitExpense operation
allows the report to be submitted
the  rst time. The ApproveExpense
operation allows the manager to
approve/deny the report:
[ServiceContract]
public interface IServiceContract
{
[OperationContract]
ExpenseMsg SubmitExpense(ExpenseMsg msg);
[OperationContract]
ExpenseMsg ApproveExpense(ExpenseMsg msg);
}
[DataContract]
[Serializable]
public class ExpenseMsg
{
[DataMember]
public string ExpenseId;
[DataMember]
public string Name;
[DataMember]
public string Status;
[DataMember]
public string Comments;
[DataMember]
public decimal Lodging;
[DataMember]
public decimal Meals;
[DataMember]
public decimal Auto;
[DataMember]
public decimal Misc;
[DataMember]
public decimal Total;
}
[
ServiceContract
]
public interface
IServiceContract
{
[
OperationContract
]
ExpenseMsg
SubmitExpense(
ExpenseMsg
msg);
[
OperationContract
]
ExpenseMsg
ApproveExpense(
ExpenseMsg
msg);
}
[
DataContract
]
[
Serializable
]
public class
ExpenseMsg
{
[
DataMember
]
public string
ExpenseId;
[
DataMember
]
public string
Name;
[
DataMember
]
public string
Status;
[
DataMember
]
public string
Comments;
[
DataMember
]
public decimal
Lodging;
[
DataMember
]
public decimal
Meals;
[
DataMember
]
public decimal
Auto;
[
DataMember
]
public decimal
Misc;
[
DataMember
]
public decimal
Total;
}
Implemented the work ow. This process received the initial expense message 4.
and then ran it against business rules, totaled the amounts, and sent it back to
the user with any warnings. The manager then could resubmit the expense with
a status of
approved or
denied. See
the work ow
and business
rules depicted
below:
The receive
shapes (green)
and send shapes
(blue) were tied
to our service
contract, our
appropriate
operation
contract,
and data
bound to our
message.
40 What’s Under That Hood?
What’s Under That Hood? 41
Once the work ow was built and GAC’d, we right 5.
clicked on the project and selected “Generate
Orchestration”. This process created the appropri-
ate schemas as well as the orchestration depicted
below:
Because we had multiple send/receives, we added 6.
a property schema and created a correlation set
to allow the second receive to follow the  rst
send shape based on the unique identi er for the
expense report. In other words, we turned our WF
work ow into a BizTalk long running transaction.
The solution was then deployed to BizTalk and con-7.
gured. The port was bound to a Request-Response
WCF-NetTCP receive location.
The test harness was modi ed to call the orchestra-8.
tion through code like the following:
NetTcpBinding b = new NetTcpBinding();
EndpointAddress ea =
new EndpointAddress("net.tcp://localhost:5000/Expense");
ChannelFactory<IServiceContract> cf =
new ChannelFactory<IServiceContract>(b, ea);
cf.Open();
IServiceContract sc = cf.CreateChannel();
ExpenseMsg m = new ExpenseMsg();
getMessageFromTextBoxes(m);
m = sc.SubmitExpense(m);
writeMessageToTextBoxes(m);
NetTcpBinding
b =
new
NetTcpBinding
();
EndpointAddress
ea =
new
EndpointAddress
(
"net.tcp://localhost:5000/Expense"
);
ChannelFactory
<
IServiceContract
> cf =
new
ChannelFactory
<
IServiceContract
>(b, ea);
cf.Open();
IServiceContract
sc = cf.CreateChannel();
ExpenseMsg
m =
new
ExpenseMsg
();
getMessageFromTextBoxes(m);
m = sc.SubmitExpense(m);
writeMessageToTextBoxes(m);
With that we had a functioning work ow process hosted in BizTalk Server. This
example combined the features of BizTalk and WF in a highly-scalable environment
that hosts long running business processes.
So we’ve seen some of the features of WF we can use in BizTalk, but how about we
extend some bene ts of BizTalk to WF? Business activity monitoring seems like a
pretty good candidate. If we implemented this example completely in an BizTalk
orchestration, we could use BAM and the BAM Portal to surface information about
this process. How do we get this visibility when using WF?
Providing Business Visibility With
the Bam Interceptor for WF
In BizTalk 2006 R2, Microsoft released interceptors that allow users to track BAM
data outside of BizTalk. Interceptors were delivered for WF and. The WF BAM
interceptor can be used to track business data about our expense process. There
are really a few steps to implement this:
Create a BAM Observation Model as with any BAM solution. Deploy using the 1.
BM.exe command line tool.
The application must register the BAM Tracking service with the Work ow-2.
Runtime. I did this by making a slight modi cation to the InitRuntime method
of the WFHost class (This is the class that really hosts the work ow within the
orchestration). I believe this could also be done through pure con guration,
but last time I modi ed the BTSNTSvc.exe.con g  le, the building I was in went
dark and I had to reset all the circuits. The lines that were added to the code to
register the BAM tracking service were:
System.Collections.Specialized.NameValueCollection serviceParams = new
System.Collections.Specialized.NameValueCollection();
serviceParams.Add(“ConnectionString”, “Integrated Security=SSPI;Data Source=.;Initial
Catalog=BAMPrimaryImport”);
r.AddService(new BamTrackingService(serviceParams));
System.Collections.Specialized.NameValueCollection serviceParams = new
System.Collections.Specialized.NameValueCollection();
serviceParams.Add(“ConnectionString”, “Integrated Security=SSPI;Data Source=.;Initial
Catalog=BAMPrimaryImport”);
r.AddService(new BamTrackingService(serviceParams));
Create an interceptor con guration XML  le and tell BAM what to track. Deploy 3.
this via the BM.exe command line tool. [This is the hard part, reverse polish
notation just sounds a little unintuitive, doesn’t it?] See the following excerpt of
the con guration  le:
<ic:InterceptorConfiguration xmlns:ic=”http://schemas.microsoft.com/BizTalkServer/2004/10/BAM
InterceptorConfiguration” xmlns:wf=”http://schemas.microsoft.com/BizTalkServer/2004/10/BAM
WorkflowInterceptorConfiguration”>
<ic:EventSource Name=”WorkflowExpense” Technology=”WF” Manifest=”ExpenseWorkflow.WorkflowExpense,
ExpenseWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4b46ec1dfc9a2ea5”/>
<ic:BamActivity Name=”ExpenseActivity”>
<ic:OnEvent Name=”calculateTotal” Source=”WorkflowExpense” IsBegin=”true” IsEnd=”false”>
<ic:Filter>
<ic:Expression>
<wf:Operation Name=”GetActivityName” />
<ic:Operation Name=”Constant”>
<ic:Argument>calculateTotal</ic:Argument>
</ic:Operation>
<ic:Operation Name=”Equals” />
</ic:Expression>
</ic:Filter>
<ic:CorrelationID>
<ic:Expression>
<ic:InterceptorConfiguration xmlns:ic=”http://schemas.microsoft.com/BizTalkServer/2004/10/BAM
InterceptorConfiguration” xmlns:wf=”http://schemas.microsoft.com/BizTalkServer/2004/10/BAM
WorkflowInterceptorConfiguration”>
<ic:EventSource Name=”WorkflowExpense” Technology=”WF” Manifest=”ExpenseWorkflow.WorkflowExpense,
ExpenseWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4b46ec1dfc9a2ea5”/>
<ic:BamActivity Name=”ExpenseActivity”>
<ic:OnEvent Name=”calculateTotal” Source=”WorkflowExpense” IsBegin=”true” IsEnd=”false”>
<ic:Filter>
<ic:Expression>
<wf:Operation Name=”GetActivityName” />
<ic:Operation Name=”Constant”>
<ic:Argument>calculateTotal</ic:Argument>
</ic:Operation>
<ic:Operation Name=”Equals” />
</ic:Expression>
</ic:Filter>
<ic:CorrelationID>
<ic:Expression>
42 What’s Under That Hood?
What’s Under That Hood? 43
<wf:Operation Name=”GetWorkflowProperty”>
<wf:Argument>Expense</wf:Argument>
</wf:Operation>
</ic:Expression>
</ic:CorrelationID>
<ic:Update DataItemName=”Submitted” Type=”DATETIME”>
<ic:Expression>
<wf:Operation Name=”GetContextProperty”>
<wf:Argument>EventTime</wf:Argument>
</wf:Operation>
</ic:Expression>
</ic:Update>
</ic:OnEvent>
Once those steps are completed and
everything is deployed, the data will be
tracked and can be viewed in the BAM
Portal as shown below:
<wf:Operation Name=”GetWorkflowProperty”>
<wf:Argument>Expense</wf:Argument>
</wf:Operation>
</ic:Expression>
</ic:CorrelationID>
<ic:Update DataItemName=”Submitted” Type=”DATETIME”>
<ic:Expression>
<wf:Operation Name=”GetContextProperty”>
<wf:Argument>EventTime</wf:Argument>
</wf:Operation>
</ic:Expression>
</ic:Update>
</ic:OnEvent>
So in the end, we were able to treat
our BizTalk hosted work ow just like an
orchestration. In fact, with continua-
tion, it really opens the possibilities of
implementing solutions with both.
Debugging the Solution
Another nice feature of the exten-
sion SDK is the ability to debug the
work ow in Visual Studio. This can
be done by setting a breakpoint and
then attaching the debugger to the
BTSNTSvc process. When the orches-
tration then initiates the work ow, the
breakpoint is hit as shown below:
Obviously, this tight integration with
Visual Studio is expected for WF, but
is not something us BizTalk developers
are very used to.
The Benefits In The Now
The expense report example was good for a short article, but besides being new
and cool technology, why would we start using this today?
WF developers can take advantage of the enterprise BizTalk platform without
knowing the intricacies of orchestration development. Development teams
can realize the productivity gains of developing in WF over orchestrations for
complex business logic. A smaller subset of BizTalk developers can hook up
the plumbing where necessary. WF is much more dynamic and can provide for
some robust solutions that just can’t be done very easily in pure BizTalk.
Many solutions require complex validation and manipulation of messages. I’ve
seen solutions to accomplish this using the BRE, custom web services, .NET
helper components, complex maps, etc. The ability to do some of this in a WF
based solution just gives us one more tool to be able to our toolbox for solving
these types of problems.
This SDK can help us reuse more of our WF based solutions in BizTalk as well as
straight .NET development.
Conclusion
The BizTalk Server Extensions for Windows Work ow SDK is de nitely something
to check out. Simple work ows can be implemented with ease. However, It really
does require pretty decent BizTalk skills to be able to implement complex work-
ows effectively. The getting started guide really gives a great quick jump start.
Using WF for very complex business logic can really make life easier over complex
orchestration development. The seamless debugging in Visual Studio is great. As
we begin to hear more details about Oslo and start to realize tighter integration
between WF and BizTalk, we truly will have the best of both worlds.
44 What’s Under That Hood?