The Business Process
The first process is the Bill Upload Process:
The process is simple:
- The Gateway will receive a set of Bills from the vendor. First thing the Gateway will do is to log this message somewhere.
- The Gateway then sends the bills to the bank, and expects an immediate acknowledgment.
- If transmission error occurs, the process will retry sending for maximum of 3 times. If still the message cannot be delivered to the bank the process will terminate itself.
- The acknowledgement from the bank tells the gateways if has accepted the message; for example after verifying the schema. If the bank’s response indicates a problem, then again the process will terminate itself.
- At this stage, the process goes into a waiting state. Essentially waiting for the bank to process the bills. This can take hours or even days. After the bank finishes, it sends the result of bills processing. The result will contain any rejected bills. Now why bills would be rejected is not of our concern for this scenario. The important thing is that the process will have to extract these bills and store then in a DB, for example for future processing.
Open the BillUploadBatch.odx file:
Now let’s examine some of the functionalities of this process. As you read through the following sections, jump back to VS solution and examine how things are done. Let’s start:
Retry Rules and Exception Handling
- First, the gateway receives the set of bills. Next it sends the message to be logged. I will return back to this shape in the next section as I talk about correlation.
- Now the retry logic begins. First notice that the logic is wrapped inside a Loop shape, which is set for a maximum of 3 iterations. Now because I need to use Exception Handling, I have used a scope shape with Transaction Type set to None. Therefore, I can associate an Exception Handling block with this shape.
- Now the gateway sends the bills to the bank. It is expecting to receive an immediate response and the logical port has its Delivery Notification property set to Transmitted.
- In case a transmission error happens, an exception will be raised, which will move control to the Exception Handling block. I have configured this block to catch any System.Exception, although you can add multiple blocks to catch specific exceptions. Now in my exception block, I just use the Delay shape to wait for a certain amount of time.
- And then I update the Loop counter; remember I just want to loop 3 times. So if this counter becomes 3, the loop will exit.
- Back to the sending logic, and now assume there was no transmission error.
- However, in the SetVariables Expression shape, I read a distinguished from the bank response that tells me if the bills messages was accepted.
- If not I terminate the process.
- After the completion of all this logic, I finally check, if after 3 retries, the response is still not received, then I construct an email message, send it out, and terminate the process.
Final thing I want to highlight here, is the way I am creating the email message. I am using a message declared as XmlDocument, then I hard-coded the Xml content of this message. And finally I used one of the system properties promoted by the SMTP adapter, to set the text of the email message.
- In my process, after the gateway has sent the bills message to the bank and assuming it has received a response within the boundaries of the retry rules…
- The process now goes into a waiting state, waiting for the bank to send back the result of bills processing.
- Now as you should expect, this calls for correlation, because assuming there will be multiple running instances of my process, i need to be sure that the message coming from the bank is routed to the same Orchestration instance that sent the bills message.
To implement correlation, several steps are required.
- First in my Schemas.BillUpload project I have created a Property Schema called BillUploadPropertySchema. This schema has one field, which is RqUID. This is the field that I will base my correlation on.
- I now to define what are the outgoing and incoming messages that need to be associated with the correlation based on RqUID. Schema BillUploadRequest, contains the Request ID field and this can be my outgoing message. So, I promote the Request ID field using the property schema i showed you. The response coming back from the bank is represented by schema BillConfirmationRequest. In this schema, the ID that I need to correlate on is called Async Request ID, so again I promote this property using the same property schema
- Next step is defining CorrelationType and a coresponsing CorrelationSet. From the Ox view, I have created a correlation type which is basically nothing more than an indication about what property field I will be basing my correlation on; in which case RqUID. And a correlation set which is basically an instance of the correlation type.
- Finally, to make correlation work, I need to initialize the correlation set on the Shape sending the BillUpload request message, and then follow on this correlation set on the Shape recieving the BillCOnfirmation message from the bank.
- To do this, back to the shape which logs the request message and I set the initializing correlation set to the set I created
- And finally, in the shape which is waiting to receive the message from the bank, I follow on the same correlation set.
This creates an association between the field RqUID of the outgoing message and field AsyncRqUID of the incoming message…this association is done via the property schema. So now BizTalk knows how to hand the incoming message to the correct running Ox instance by matching the value of the two fields together.
Updating the Database: WCF-SQL LOB Adapter
Now that I have the result back from the bank. I need to inspect this result and store any rejected records in the database; for example for future correction.
For this I have a demo database (which we created when we ran the script), which contains a single table and a single stored procedure. The stored procedure will be called from the Ox, to insert the rejected records.
In order to achieve this, I will use the WCF-SQL LOB adapter. This adapter, like many others, provides the ability to generate request and response schemas from the DB structure. These schemas are then understood by the adapter which performs the required update.
To add these schemas in the solution:
- right click the project name, select Add and Add Generated Items
- Select the Add Adapter Metadata, which displays a wizard to select the required LOB adapter.
- In my case I select the WCF-SQL and hit Next.
- In the configuration wizard, select configure, and in the URI properties, supply both the initial catalog and the Server.
- Select connect to verify connection.
- And now you can see the schema of the DB retrieved.
- If you click the procedures tab you can see the stored procedure you want to call, so select it and finish the wizard.
- You then have request and response schemas generated
In the VS solution, I have deleted the response schema; in my case, the stored procedure will not be returning any result, so I can delete the response part.
Back in the Orchestration:
- I want to insert all rejected records coming from the bank in the DB.
- To do so, I have a map called BillConfirmationRq2SQLUpdate which maps each of the rejected records, to the required parameters of the stored procedure.
- I use the map inside a Transform shape to construct the SQL update message
- and then send this message out.
Handling Different Partner Schemas: Outbound Maps
Maps can be used in receive and send ports as well as orchestrations. Using maps in receive and send ports is interesting, because it gives the possibility to separate the schemas that partners do business with, and the schemas that we want to use inside a BizTalk project. This way, we do not ask a partner to comply with the schema format that we want.
So in my process, the gateway sends the bills message to the bank and gets an immediate response. The bank does not want to use any target namespace in their schemas. They had their own reasons for this, and the gateway could no nothing about it.
However, any schema in BizTalk must – or at least should – have a target namespace. To solve this, I ended up creating this Schemas.BillUpload.OutBound project. This project two schemas BillUploadRequest and BillUploadResponse. These are the schemas that are sent and received from the bank These schemas are exactly the same as the ones I defined in the Schemas.BillUpload project; expect that they contain no target namespace.
In the same project, I have two maps:
- the first map creates the outbound bill upload request (I mean the one without the namespace); it uses the mass copy functoid to copy the entire message content, so it ends up with the schema expected by the bank which does not have a target namespace
- The second map creates the inbound bill upload response. This time the source is the response message coming from the bank without the namespace, and the output is the message I expect in my process, which contains the namespace.
In the administration console send port BillUploadRqRs is the solicit-response port that sends the bills message to the bank. In the outbound maps tab I use the map that will transform the outgoing message. While In the inbound maps tab i use the map that will transform the response message from the bank
Multiple Identical Message Types Deployed: Pipeline Configuration
In the Schemas.BillUpload.OutBound project I have two schemas: BillUploadRequest and BillUploadResponse, that both has the same type. This is because both has the same Rootnodename but also both has empty targetnamespace.
Now for the xmldisassembler, this is a problem. When it receives the incoming message from the bank, it tries to indetify the type of the message, but finds two schemas of the same type deployed so it fails and raises an error.
To solve this, there are two options:
- I can either use per-instance pipeline configuration from the admin console (BillUploadRqRs port –> Receive pipeline instance configuration –> DocumentSpecNames property).
- However, I prefer the other method of creating a custom pipeline. So in the receive pipeline configuration (granted you imported the bindings as explained in the previous article) you can see I have a pipeline called ReceiveBillUploadResponse.
To see how I created this pipeline, from VS solution see project Pipelines.BillUpload. I have used the XmlDisassembler component and in the document schemas property I selected the schema that I want the message type to resolve to.
No Processing Required? Increase Performance with PassThruTransmit
An often forgotten practice by many BizTalk developers, is to decide if they actually need any processing on outgoing messages. The xml transmit pipeline is an overhead if no processing is required on outgoing messages.
So for example, if no messages need to be batched together, or no encryption need to applied, or no demotion is required – in general if no processing is required on outgoing messages – then we can just use the passthrutransmit pipeline, which will give better performance.
So, for example in my BillUploadRqRs port, when sending the bills message out, I can use the passthru transmit pipeline and let the message just flow out with no processing.
Running the Process
To run the sample, create the folder structure to drop messages for the file adapters.
- BillUploadIn is where I receive the initial bills message.
- BillUploadRqLog is where I log the message
- And BillConfirmationRequestIn is where the process will be waiting to get the result of bill processing by the bank
To test the process:
Drop the BillUploadRequest message in the BillUploadIn folder
This initiates a new instance of the Ox.
At this stage, the message must be logged, so if you check the BillUploadRqLog folder, you must see the message. The important thing to remember is that at this stage, the correlation set has been initialized.
Now, the bills message has been sent to the bank.
To simulate the bank, I have used the HTTP adapter to send the request to an ASP.NET application (BillUploadRqRs in the VS solution). The application accepts the message as HttpRequest, uses XmlTextReader and XmlDocument to read the message content, and then builds up the response using the same rquid extracted from the request. And finally sends the response back as an HttpResponse.
Just recall that what happened now simulated the bank positive acknowledgement that I has received and accepted the bills. And at this stage, the process is waiting for the billconfirmation request message; that is the result of bills processing from the bank.
Before going on, to verify that the process has been running as expected, check the dehydrated Oxs from the admin console:
Since the Ox is waiting to receive a message, the engine has dehydrated the instance instead of keeping it in memory.
You can also check the Ox Debugger and see how the process is actually waiting for the correlated message (i.e. dehydrated):
Now to resume the process (which is waiting for the BillUploadConfirmationRequest message), go the file system, make sure that the AsyncRqUID of the BillUploadConfirmationRequest is identical to the RqUID of the BillUploadRequest (because this is what the correlation is based on). Then drop the confirmation message. This completes the Ox.
You should then see in the DB table some rejected records inserted. These records are marked as rejected in the ConfirmationRequest message and are mapped to the SQL schema as I showed you before.
In the next article we’ll examine the other part of the bill upload business process – and this failed message routing.