1. Problem
You are developing an
orchestration and need to use pipeline-processing stages within the
workflow. Specifically, you need to validate a document and batch
multiple messages into a single interchange. You want the orchestration
to be efficient, minimizing the interactions between the orchestration
and the MessageBox database.
2. Solution
BizTalk Server exposes a
programmatic interface into pipelines, allowing you to call both
receive and send pipelines directly from orchestrations. Assume your
orchestration has the following steps:
Receive an order message in an industry-standard format.
Transform the message to a canonical version of the order schema.
Send the canonical order message to an external application for processing.
Receive a status message from the external application indicating the success or failure of the message processing.
In addition to the
orchestration, you have also created a receive pipeline to validate the
canonical order schema, an envelope, and a send pipeline to batch the
canonical order and order response documents into a single interchange.
Open your orchestration in Visual Studio, and perform the following steps to implement the pipeline calls:
From the toolbox, drag the following shapes onto the orchestration design surface:
Drag a Scope shape named Execute Receive Pipeline directly below the Construct Canonical Order Message shape, and set the Transaction Type property to Atomic.
The orchestration's Transaction Type property must be set to Long Running in order for it to hold an Atomic Scope shape.
|
|
Drag an Expression shape named Execute Receive Pipeline inside the Scope shape added in the previous step.
Drag a Construct Message shape named Construct Validated Message directly below the Execute Receive Pipeline Expression shape (also inside the Execute Receive Pipeline Scope shape), and set the Messages Constructed property to a message defined by your validating schema—in this solution, a message named CanonicalOrderValidatedMessage defined by the canonical order schema.
Drag a Message Assignment shape named Assign Validated Message inside the construct message shape added in the previous step.
Drag a Construct Message shape named Construct Batched Message directly below the Receive Order Status receive shape, and set the Messages Constructed property to an XML message—in this solution, a message named BatchedOrderMessage of type System.Xml.XmlDocument.
Drag a Message Assignment shape named Assign Batched Message inside the construct message shape added in the previous step.
Add a reference in your BizTalk project to the Microsoft.XLANGs.Pipeline.dll (found in the root BizTalk installation folder), which holds the classes required for calling pipelines from orchestrations.
Create a new variable named ValidatePipelineOutput of type Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages in the Execute Receive Pipeline
Scope shape. This variable will hold the validated output of the call
to the receive pipeline, containing the validated canonical order
message.
Add the following code to the Execute Receive Pipeline Expression shape in order to call the appropriate receive pipeline:
ValidatePipelineOutput = Microsoft.XLANGs
.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline
(typeof(OrderProcessing.ValidateCanonical
OrderSchemaReceivePipeline),
CanonicalOrderMessage);
Add the following code to the Assign Validated Message shape in order to extract the validated canonical order message:
CanonicalOrderValidatedMessage = null;
ValidatePipelineOutput.MoveNext();
ValidatePipelineOutput.GetCurrent(CanonicalOrderValidatedMessage);
NOTE
To handle multiple output
messages from a receive pipeline (when using an envelope for message
debatching, for example), you may need to use a loop in order to iterate
through the message collection and handle each appropriately.
Create a new variable named BatchingPipelineInput of type Microsoft.XLANGs.Pipeline.SendPipelineInputMessages. This variable will hold the output of the call to the send pipeline, containing the batched order messages.
Add the following code to the Assign Flat File Message shape to call the appropriate send pipeline (BatchedOrderMessages is a BizTalk message of type System.XML.XmlDocument and is used to hold the output of the ExecuteSendPipeline method call):
BatchedOrderMessages = null;
BatchingPipelineInput.Add(CanonicalOrderValidatedMessage);
BatchingPipelineInput.Add(CanonicalOrderStatusMessage);
Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteSendPipeline
(typeof(OrderProcessing.BatchOrderAndStatusMessagesSendPipeline),
BatchingPipelineInput, BatchedOrderMessages);
Build and deploy the solution.
3. How It Works
This solution shows how to
leverage pipeline processing directly from orchestrations. First, we
discuss the mainline process involved in the solution. Then, we discuss
exception handling.
3.1. Mainline Process
The Microsoft.XLANGs.Pipeline.XLANGPipelineManager class exposes an API for calling pipelines programmatically.
NOTE
To use this class, you must add a reference in your BizTalk project to the Microsoft.XLANGs.Pipeline assembly.
In this scenario, you needed to
use both a receive pipeline and a send pipeline. The receive pipeline
allows you to validate a message that you create in the orchestration.
You call the receive pipeline via the ExecuteReceivePipeline method, which has the following signature:
Type: The fully qualified type of the receive pipeline you need to call. You format this parameter as typeof(FullyQualifiedNameOfReceivePipeline).
You can easily find the fully qualified type name of your receive
pipeline by clicking the pipeline file in the Solution Explorer and
viewing the Fully Qualified Name property.
XLANGMessage: The XLANG message object you need to pass into the receive pipeline.
This method returns an instance of the Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages
class, which contains an enumeration of output messages from the
receive pipeline. In this solution, the pipeline produces a single
message, and you are able to easily extract the message by using the MoveNext and GetCurrent methods of the class. If multiple messages could be returned by the receive pipeline, you would need to loop through the ReceivePipelineOutputMessages object to extract each message. You can accomplish this by using a Loop shape configured with ValidatePipelineOutput.MoveNext() as the expression and an Assign Message shape configured with ValidatePipelineOutput.GetCurrent(CanonicalOrderValidatedMessage); as the expression.
NOTE
You must place the code
(usually contained within an Expression shape) that calls the receive
pipeline in an Atomic Scope shape, because the ReceivePipelineOutputMessages class is not serializeable and cannot be persisted to the MessageBox.
The send pipeline
batches multiple messages within the orchestration into a single
interchange. You call the send pipeline via the ExecuteSendPipeline method, which has the following signature:
Type: The fully qualified type of the send pipeline you need to call. You format this parameter as typeof(FullyQualifiedNameOfSendPipeline).
SendPipelineInputMessage: The collection of messages you need to pass into the send pipeline.
XLANGMessage: The XLANG message object you will receive as output from the send pipeline.
NOTE
You must place the code that calls the send pipeline in a Message Assignment shape, because the ExecuteSendPipeline method constructs the XLANGMessage it passes as an output parameter.
Although calling pipelines from
an orchestration generally results in the same functionality and
processing as when they are executed via BizTalk messaging objects, you
need to be aware of some differences. One of the most important
discrepancies is the input and output XLANG message objects that are
passed to ExecuteReceivePipeline and ExecuteSendPipeline, respectively. Although XLANG message objects allow you define them as being of any type (XmlDocument, String, DateTime,
and so on), the XLANG message instances you pass to the pipeline
execute methods must be XML documents. This means that the XLANG message
objects should be defined by either the System.Xml.XmlDocument
type or an XML schema to ensure consistent behavior. This limitation is
because the pipeline execution methods treat their XLANG message
parameters as XmlDocument objects. Although it is possible to use a different type to define your XLANG message objects (such as System.String),
it is advised you do so with caution and careful consideration and
validate that any data written to the message is in XML format.
NOTE
One shortcoming of
this limitation is that it is not straightforward to call a send
pipeline that assembles an XML document into flat file format, because
the flat file string is returned as an XmlDocument object but likely does not conform to XML.
Another difference to be aware
of is how tracking and monitoring data is handled. When calling
pipelines from an orchestration, pipeline components leveraging the BAM
Interceptor API are not supported, and the assembler/disassembler stages
do not process tracking information. Additionally, transactional
pipeline components are not supported when called from orchestrations.
3.2. Exception Handling
Any errors occurring in pipelines called from orchestrations cause an XLANGPipelineManagerException
exception object to be thrown. Messages are not suspended, as they
would be if the pipeline were executed by a messaging object (receive
location or send port). This exception object can be handled via a catch
exception block, which is configured on a Scope shape within an
orchestration. Once the exception is caught, the exception's properties
can be interrogated and the event handled appropriately.
Using an invalid document (using a character in the Identifier element, which expects an integer value) in this solution produces the following error:
There was a failure executing pipeline "OrderProcessing.ValidateCanonicalOrderSchema
ReceivePipeline".
Error details:
"The 'Identifier' element has an invalid value according to its data type.".
Exception type: XLANGPipelineManagerException
Source: Microsoft.XLANGs.Pipeline
Target Site: Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages ExecutePipeline
(Microsoft.BizTalk.PipelineOM.ReceivePipeline, Microsoft.
XLANGs.BaseTypes.XLANGMessage)
As you can see, the exception thrown has a type of XLANGPipelineManagerException, and the description indicates the specific validation failure.