Introduction
This article is the second installment of a series on the scenario used in SAP BAPI Transactions Walkthrough, which should be read before this blog post.
Here, instead of receiving a pre-determined number of BAPI transaction requests one at a time, the orchestration expects a single document containing a batch of requests, as well as the desired outcome (commit or rollback). Debatching of XML messages into single BAPI transaction requests is done inside the orchestration by calling the default XMLReceive pipeline. General documentation regarding this orchestration feature may be found at How to Use Expressions to Execute Pipelines.
The choice of in-orchestration pipeline processing over direct processing in the receive port is mostly arbitrary. It allows us to keep this presentation focused on a single place where the different stages are integrated. The orchestration is well-suited for a workflow of multiple actions and custom error handling. There are pros and cons mentioned in the literature regarding both options and it would take another post to cover them in detail.
It is worth mentioning that with in-orchestration pipelines:
- Transforms can happen before debatching, which in some cases could be a hard requirement;
- It is possible to enumerate over messages of different types;
- Failures result in exceptions being thrown rather than messages being suspended by the pipeline (see for instance Error Handling for failure behavior in the pipeline stage). The thrown exceptions can be handled in a catch block within the calling orchestration, which could allow for graceful error handling with BAPI_TRANSACTION_ROLLBACK.
Debatching in the receive location is presented in Recovering SAP BAPI Transactions with Custom Pipelines.
1. Debatching with Receive Pipeline
Pipeline processing of messages containing batches of orders can summarized as :
1.1 Envelope Schema
The first step for using a debatching pipeline is to define an envelope schema to represent multiple orders. This is done according to the following process, initially documented in Calling a pipeline inside an orchestration:
- Add a new schema file to the project.
- Change the root node name to "Orders".
- In the properties window for the <schema> treeview node: (a) change the Envelope property to Yes, and (b) import the Order schema.
- In the properties window for the "Orders" root node, set the Body XPath property to /*[local-name()='Orders' and namespace-uri()='<namespace of the schema here'
- Insert a child record under the root node.
- In the child record properties, set the data structure type field to Order. As a result, the Order definition will appear under Orders.
Messages initially received by the orchestration contain BAPI transaction requests, which contain an IsCommit field in addition to the envelope schema.
IsCommit is a promoted property which is used later on for deciding whether to commit or rollback the transaction batch. Orders corresponds to the envelope schema imported into RequestsInfo. The Orders element is extracted by a transform shape into a message called SalesOrders.
Note: the RequestsInfo schema could also be an envelope schema for Order, instead of the Orders schema. |
1.2 Implementing the Pipeline
Before using the pipeline, the following dependencies must be added to the BizTalk project:
- Microsoft.XLANGs.Pipeline.dll
- Microsoft.BizTalk.Pipeline.dll
The pipeline must be inside a scope of type Atomic because it is of non-serializable type. As a result, the orchestration has to be of type "Long running". Inside the atomic scope, the first expression starts the pipeline execution:
PipelineOutputMessageVar = Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline(typeof(Microsoft.BizTalk.DefaultPipelines.XMLReceive), SalesOrders);
where PipelineOutputMessageVar is a variable of type Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages and is used to iterate in a loop shape with the expression:
PipelineOutputMessageVar.MoveNext()
Inside the loop shape, a message of type Order, CurrentOrder, is assigned as follows:
CurrentOrder = null;
PipelineOutputMessageVar.GetCurrent(CurrentOrder);
CurrentOrder is then mapped to a message (BAPIMessage) of type BUS2032.CREATEFROMDAT2.
The figure below shows the entire pipeline stage:
2. Caching BAPI Messages
Ideally, the BAPIMessages should be sent to the SAP server from within the pipeline, by using the send-receive port connected to SAP. However, an atomic scope may not contain both the send and the corresponding receive (see a detailed explanation here); in our case, the SAP adapter port needs to be bidirectional because the orchestration needs the document id contained in the response and which is used later on to query the transactions status. Note that this is specific to our scenario because we need a bi-directional port rather than just sending BAPI transactions, which could be a perfectly valid and simpler scenario.
To work around the scope constraint, BAPIMessages are saved in a list object to be used outside the pipeline scope:
TempXmlData = BAPIMessage;
BAPIOrders.AddFromXMLString(TempXmlData);
where TempXmlData is a variable of type XmlDocument, and BAPIOrders is of custom type BAPIOrderList added in a helper library.
namespace SapBAPITxClientDebatching
{
[Serializable]
public class BAPIOrdersList : List<CREATEFROMDAT2>
{
public BAPIOrdersList() { }
public void AddFromXMLString(XmlDocument document)
{
MemoryStream stream = new MemoryStream();
document.Save(stream);
stream.Flush();
stream.Position = 0;
XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(typeof(CREATEFROMDAT2));
StreamReader st = new StreamReader(stream);
this.Add((CREATEFROMDAT2)reader.Deserialize(st));
st.Close();
}
public CREATEFROMDAT2 Get(int index)
{
return this[index];
}
public int OrdersCount()
{
return this.Count;
}
}
}
The helper library also contains C# classes corresponding to the BAPI transactions schemas generated by running the xsd.exe utility on the BAPI schemas generated in Visual Studio.
3. Processing the Debatched BAPI Transactions
Once all Order elements have been debatched, the orchestration exits the pipeline atomic scope and iterates over the list of BAPI transaction messages to serialize and send to the send-receive port (LOBPort) connected to the SAP server. This stage is identical to Stage 2 in SAP BAPI Transactions Tutorial except that here, BAPI transaction requests are provided by the orchestration variable BAPIOrders.
4. The Global Picture
Subsequent stages of the orchestration are presented in detail in SAP BAPI Transactions Tutorial. In Stage 3, the action to take (Commit/Rollback) is determined by the IsCommit promoted property mentioned earlier.
5. Concluding Remarks
The aim of this post was to extend the BAPI tutorial scenario by showing BAPI transactions working with known BizTalk patterns such as debatching. Another pattern to consider is Scatter-Gather, of which debatching is the first step. This will be the topic of another blog post (Scatter-Gather Pattern with SAP BAPI Transactions).
All code used in this article is attached.
References
SAP BAPI Transactions Walkthrough
Calling a pipeline inside an orchestration
Performance study of debatching within an orchestration
Different techniques of debatching in orchestration
How to Use Expressions to Execute Pipelines
Scatter-Gather Pattern with SAP BAPI Transactions
Recovering SAP BAPI Transactions with Custom Pipelines
For more general info on the SAP Adapter:
Run BAPI Transactions in SAP using BizTalk Server
Message Schemas for BAPI Operations
Registry setting to enable BAPI transactions
Get Metadata for SAP Operations in Visual Studio
Browse, search, and retrieve metadata from SAP for BAPI operations