Recently I've received an exception in the event viewer when trying to access an Oracle procedure via WCF-OracleDb adapter:
As you can see, I'm getting an ORA-XXXXX exception and the LOB adapter is throwing Microsoft.ServiceModel.Channels.Common.TargetSystemException. That means there was a problem executing the operation on the ERP.
But how do I catch this kind of exception in my orchestration ?
First of all, it's important to set the "Delivery Notification" property on the port configuration to "Transmitted" (In order to notify the orchestration in case of transmission failure).
The second part of catching the exception is not quite a straight-forward matter.
I've tried to catch Microsoft.ServiceModel.Channels.Common.TargetSystemException exception in my orchestration (after adding references to Microsoft.ServiceModel.Channels and System.ServiceModel) butit doesn't seem to work (Because you can only handle SOAP exceptions thrown from the port).
Second shot was to try catch a "General Exception". We use it to catch exceptions which are not derived from System.Exception. That's the reason why you can't get an exception object or any details on the exception.
So I did managed to catch the exception, but still wanted to get information on the exception.
Third and last shot! I've attached to the BizTalk service process: "BTSNTSVC.exe" using Microsoft Visual Studio, and ran the orchestration in order to get this exception again.
This way I got more accurate information on the exception rather than reading the trace log in the event viewer:
-->The exception type is: 'Microsoft.XLANGs.Core.XlangSoapException'.
When you look for this type in the MSDN, you can see it derived from System.Exception.
BUT, as mentioned before - you can't catch System.Exception because it's thrown from the port and (or any other classes it derived from) it isn't a SOAP exception.
In conclusion - you have to catch 'Microsoft.XLANGs.Core.XlangSoapException'.
I've wanted to configure this as the 'Exception Object Type' in the catch exception scope of my orchestration, but couldn't find it on the .NET Exceptions list, so I had to do a little trick here:
1. I've added a catch scope with 'System.Exception' type (Basically, you can choose whatever type you want).
2. I've opened the orchestration .odx file using notepad, and modified the XLANG code by
changing each occurrence of 'System.Exception' to 'Microsoft.XLANGs.Core.XlangSoapException':
Case scenario for a BizTalk developer:
You need to receive a flat file through a receive location, and one of the customer requirements is
to save the file. This flat file should go through a custom pipeline with ‘Flat File Disassembler’ component in order to create flat file schema.
Reminder: when using BizTalk 'FILE' adapter, it deletes the file from the file system or network share.
So how can we solve this issue ?
- The idea to create a 'Send port' pointing to target location and using filters - would work only if the file data passed the pipeline stage successfully (include parsing and flat file disassembler).
In addition, we should use send pipeline with 'Flat file assembler' (Transforming
flat file schema back to the original flat file data). This will require another send pipeline for each application.
- We can use 'PassThruReceive' pipeline on a Receive Port, which will transform the flat file to XML and send it to the MessageBox. In addition, we will create a Send Port (‘PassThruTransmit’ Pipeline) with a filter on the Receive Port, which will send XML file to an archive folder we’ll choose. After this step, because we need to use ‘Flat File Disassembler’ on that file, we will create another send port which will send it to another location (The process will start to run from that folder). So that way is possible, but it requires two more Send Ports for the application. Here is the diagram for that solution:
--> -->
-->In conclusion: Getting an out of the box solution is not so elegant in that case.
Let's go for developing a simple component for our custom pipeline:
'Save File Assembler Component'. The only way to add custom functionality to a ReceivePort is through an adapter or a pipeline component. Writing a custom FILE adapter would be complicated, so using a custom pipeline component is the better solution.
1. Add a 'Class Library' project named 'SaveFilePipelineComponent'.
2. Implement those interfaces:
IBaseComponent - in order to define the name, version and description of the component.
IComponentUI - in order to use the pipeline component within the Pipeline Designer environment.
Microsoft.BizTalk.Component.Interop.IComponent - in the 'Execute' method, we will process
the input message.
IPersistPropertyBag - in order to configure our pipeline at Design Time.
save the file to a new location.
catch (ArgumentException argEx) // IMPORTANT! Handles the first initialize case (When adding the component at Design Time) - all the properties are null and the exception is catched but not thrown!
-->Third step: Getting the target path by using 'AdapterFilePath' class. This class performs quite simple, so I won't get in to it. In general, the class gathers the information it needs (i.e: file name, adapter type) from the message context and builds the target path:
using (TextWriter writeMsgTextWriter = newStreamWriter(targetPath))
{
writeMsgTextWriter.Write(xmlReceived);
writeMsgTextWriter.Flush();
}
}
-->
5. Build 'SaveFilePipelineComponent' project and place the dll at:
C:\Program Files\\Pipeline Components
6. Restart Visual Studio.
7.
- Right click on the toolbox --> 'Choose Items'.
- Add the component from 'BizTalk Pipeline Components'.- Drag the component to the 'Decode' stage:
-->The component is built for the 'Decode' stage for two reasons:
1. It's the earliest stage, and we want to keep the original file.
2. We can run several components on that stage. That means the BizTalk engine
will run several 'Execute' methods on different components placed on that stage ('Execution mode' is set to 'All').