Anyone who has used the Tracking Profile Editor (TPE) in BizTalk to map field values to their BAM activity definitions knows how simple and easy the interface is to use. You simply right-click on the orchestration shape that sends or receives the message, select “Message payload…” to see the schema for the message, and then click & drag the schema node onto the activity property that you want to assign the node value to:

Easy, right? Of course. But unfortunately, like most things in life, the trade-off of simplicity is some significant limitation in functionality and/or flexibility.
In the example case above, the “B2GResponse” record is a repeating element in the schema (maxOccurs=”unbounded”). While the TPE will very happily let you save and deploy the tracking profile, you will discover that a record with multiple “B2GResponse” nodes incurs a runtime error, as evidenced by this event log entry:

The “AmbiguousXPathException” thrown states “The result set for the XPath expression ‘<xpath expression for the target “PpsrBatchId” node>‘ contains more than a single node.”
Moreover, the actual node value is never recorded, even though BAM does (very politely) create the record despite the error (it just inserts NULL into the “PpsrBatchID” field):

Essentially the XLANG engine cannot map a repeating value to a single column in the BAM activity table. Makes sense, right? Even though we know in this case that “PpsrBatchID” node will contain the same value from every record, we can’t expect the BAM runtime to infer this for us. What would be nice is if the interface allowed you to specify a specific index for the node from which to draw the value from; it doesn’t (simplicity vs. functionality).
One obvious solution would be to re-design the message schema and elevate the “PpsrBatchId” value to a single node occurrence at the message level. However, we don’t always have this option to customise messages.
It doesn’t have to end here, though. Often the limitations offered with a simple GUI tool can be overcome by looking at what’s under the covers. In this case, with a little bit of “hacking”, we can manipulate BAM to parse the value out of a specific instance of a repeating field.
First we have to understand what the TPE does. When you save a tracking profile, it creates an XML file with a “*.btt” extension. This can then be deployed either through the TPE GUI or by the BTTDeploy command line tool:
“%BTSINSTALLPATH%tracking\bttdeploy.exe” “.\ ProcessRegistrationsResponse.v0.2.btt”
If we inspect the *.btt XML file, we can easily divine its structure:

Essentially, we see a “Dimension” element for each BAM activity field with a “DataLevel” sub-element that defines the source of the data. In the case of example TPE above:
<?xml
version=“1.0“
encoding=“utf-16“?>
<TrackingProfile
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
xmlns:xsd=“http://www.w3.org/2001/XMLSchema“
VersionGuid=“00000000-0000-0000-0000-000000000000“
Name=“ProcessRegistrationsResponse“>
<Dimension
Name=“ActivityID“
DataType=“TraceID“ />
<Dimension
Name=“StartTime“
DataType=“DATETIME“>
<DataLevel
Name=“Rcv_RegistrationsResponseMsg“
SourceTypeSelected=“Orchestration Shape“
TargetAssemblyName=“MyClient.Ppsr.Orchestrations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d9cf9d5c9f7e687e“
OrchestrationReference=“MyClient.Ppsr.Orchestrations.ProcessRegistrationsResponse“
ShapeID=“5121a798-8e5d-4cc5-b0b7-ae68ece3992d“ />
</Dimension>
<Dimension
Name=“EndTime“
DataType=“DATETIME“>
<DataLevel
Name=“TRACE“
SourceTypeSelected=“Orchestration Shape“
TargetAssemblyName=“MyClient.Ppsr.Orchestrations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d9cf9d5c9f7e687e“
OrchestrationReference=“MyClient.Ppsr.Orchestrations.ProcessRegistrationsResponse“
ShapeID=“e1f75b0c-dfc4-4adb-b806-beab9156a04b“ />
</Dimension>
<Dimension
Name=“PpsrBatchID“
DataType=“NVARCHAR“>
<DataLevel
Name=“PpsrBatchID“
SourceTypeSelected=“Orchestration Payload“
TargetAssemblyName=“MyClient.Ppsr.Orchestrations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d9cf9d5c9f7e687e“
OrchestrationReference=“MyClient.Ppsr.Orchestrations.ProcessRegistrationsResponse“
ShapeID=“67801549-63ff-4db0-affa-abed2e387b22“
MessageName=“msgB2GResponsesIN“
MessagePart=“responses“
SchemaName=“MyClient.Ppsr.Schemas.Internal.PpsrDB.Veda.TableOperation_dbo_B2GResponse+Insert“
MessageDirection=“Out“
SomXPath=“/*[local-name()='<Schema>' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[local-name()='Insert' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[local-name()='Rows' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[local-name()='B2GResponse' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo']/*[local-name()='PpsrBatchID' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo']“
XPath=“/*[local-name()='Insert' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[local-name()='Rows' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[local-name()='B2GResponse' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo']/*[local-name()='PpsrBatchID' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo']“ />
</Dimension>
<Dimension
Name=“TableInsertRequestSent“
DataType=“DATETIME“>
<DataLevel
Name=“Snd_InsertResponseSQL“
SourceTypeSelected=“Orchestration Shape“
TargetAssemblyName=“MyClient.Ppsr.Orchestrations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d9cf9d5c9f7e687e“
OrchestrationReference=“MyClient.Ppsr.Orchestrations.ProcessRegistrationsResponse“
ShapeID=“67801549-63ff-4db0-affa-abed2e387b22“ />
</Dimension>
</TrackingProfile>
Notice the highlighted XPath statement that defines where to retrieve the data from for the “PpsrBatchId” field. Here we have the opportunity to manually edit the XPath, instructing BAM that we only want the first record instance by using the XPath “position()” function:
“/*[local-name()='<Schema>' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/B2GResponse']/*[
...
/*[local-name()='B2GResponse' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo' and position()=1]/*[local-name()='PpsrBatchID' and namespace-uri()='http://schemas.microsoft.com/Sql/2008/05/Types/Tables/dbo']“
Now we can save & deploy the BTT file, and the next time we run the process…

Bingo! We can now see the value entered in the table.
Lesson learned: when the simplicity of a GUI tool restricts your ability to do something slightly more advanced, it pays to dive under the covers and see what really makes it work. You’ll often be rewarded with the ability to customise the behaviour.