Biztalk 2006 Suspended Message Routing and Error Reporting

I may need to use the code in this article very soon. I am using BTS 2006 to handle EDI transactions. When things are on the happy path, generating a 997 Ack is easy. It is when the message breaks and will not parse in the flat-file schema that it gets a little sticky. I can take these messages that have been suspended, run them through a “permissive” version of the edi flat-file schema, and then use that instance to create a 997 NAck. That is a possibility I am entertaining anyway.

Biztalk 2006 Suspended Message Routing and Error Reporting

Biztalk Send Port File Name Macros

If you ever wanted to know how to use any other file name macros other than %MessageID% here they are:

%datetime%
Coordinated Universal Time (UTC) date time in the format YYYY-MM-DDThhmmss (for example, 1997-07-12T103508).

%datetime_bts2000%
UTC date time in the format YYYYMMDDhhmmsss, where sss means seconds and milliseconds (for example, 199707121035234 means 1997/07/12, 10:35:23 and 400 milliseconds).

%datetime.tz%
Local date time plus time zone from GMT in the format YYYY-MM-DDThhmmssTZD, (for example, 1997-07-12T103508+800).

%DestinationParty%
Name of the destination party. The value comes from message the context property BTS.DestinationParty.

%DestinationPartyID%
Identifier of the destination party (GUID). The value comes from the message context property BTS.DestinationPartyID.

%DestinationPartyQualifier%
Qualifier of the destination party. The value comes from the message context property BTS.DestinationPartyQualifier.

%MessageID%
Globally unique identifier (GUID) of the message in BizTalk Server. The value comes directly from the message context property BTS.MessageID.

%SourceFileName%
Name of the file from where the File adapter read the message. The file name includes extension and excludes the file path, for example, foo.xml. When substituting this property, the File adapter extracts the file name from the absolute file path stored in the FILE.ReceivedFileName context property. If the context property does not have a value, for example, if message was received on an adapter other than File adapter, then the macro will not be substituted and will remain in the file name as is (for example, C:\Drop\%SourceFileName%).

%SourceParty%
Name of the source party from which the File adapter received the message.

%SourcePartyID%
Identifier of the source party (GUID). The value comes from the message context property BTS.SourcePartyID.

%SourcePartyQualifier%
Qualifier of the source party from which the File adapter received the message.

%time%
UTC time in the format hhmmss.

%time.tz%
Local time plus time zone from GMT in the format hhmmssTZD (for example, 124525+530).

Source:
http://www.codeproject.com/useritems/SendHandlerMacro.asp

Group Aggregation in Biztalk 2004 Maps

I had a scenario where I need to group and aggregate values in a BizTalk 2004 map based on several like fields. I put together a prototype that uses an xslt technique known as “Muenchian Grouping.” The overview is that it uses an xsl:key to identify xml nodes based on specific values. Below is the code that makes up the prototype…

Source xml:

<PurchaseOrders>
<PurchaseOrder>
<From>John</From>
<To>Surrendar</To>
<PONumber>1</PONumber>
<LineItems>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens type 2</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens type 2</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens type 2</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
</LineItems>
</PurchaseOrder>
<PurchaseOrder>
<From>John</From>
<To>Surrendar</To>
<PONumber>2</PONumber>
<LineItems>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable type 2</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
</LineItems>
</PurchaseOrder>
</PurchaseOrders>

A screen-shot of the map is attached to the bottom of this post.

This is the xslt that I am running in a script functiod call-template:

<xsl:template name=”POTransform”>
<xsl:param name=”PONumber” />
<xsl:for-each select=”//LineItem[
generate-id()=generate-id(key(‘line-key’, concat($PONumber,ItemCode,Description))[1])]”>
<xsl:apply-templates select=”current()” />
</xsl:for-each>
</xsl:template>
<xsl:template match=”LineItem”>
<xsl:variable name=”LineItemList” select=”//LineItem[(ItemCode = current()/ItemCode)
and (PONumber = current()/PONumber) and (Description = current()/Description)]” />
<xsl:call-template name=”BuildLineItem”>
<xsl:with-param name=”list” select=”$LineItemList” />
</xsl:call-template>
</xsl:template>
<xsl:template name=”BuildLineItem”>
<xsl:param name=”list” />
<xsl:param name=”PONumberParam” />
<xsl:element name=”LineItem”>
<xsl:element name=”PONumber”>
<xsl:value-of select=”$list/PONumber” />
</xsl:element>
<xsl:element name=”ItemCode”>
<xsl:value-of select=”$list/ItemCode” />
</xsl:element>
<xsl:element name=”Description”>
<xsl:value-of select=”$list/Description” />
</xsl:element>
<xsl:element name=”Price”>
<xsl:value-of select=”$list/Price” />
</xsl:element>
<xsl:element name=”Quantity”>
<xsl:value-of select=”sum($list/Quantity)” />
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:key name=”line-key” match=”LineItem” use=”concat(PONumber,ItemCode,Description)” />

And finally, this is the output xml:

<PurchaseOrders>
<PurchaseOrder>
<From>John</From>
<To>Surrendar</To>
<PONumber>1</PONumber>
<LineItems>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens</Description>
<Price>1.00</Price>
<Quantity>2</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens type 2</Description>
<Price>1.00</Price>
<Quantity>3</Quantity>
</LineItem>
<LineItem>
<PONumber>1</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
</LineItems>
</PurchaseOrder>
<PurchaseOrder>
<From>John</From>
<To>Surrendar</To>
<PONumber>2</PONumber>
<LineItems>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>ScreenCleaner</ItemCode>
<Description>Cleans computer screens</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable</Description>
<Price>1.00</Price>
<Quantity>3</Quantity>
</LineItem>
<LineItem>
<PONumber>2</PONumber>
<ItemCode>NetworkCable</ItemCode>
<Description>Computer network cable type 2</Description>
<Price>1.00</Price>
<Quantity>1</Quantity>
</LineItem>
</LineItems>
</PurchaseOrder>
</PurchaseOrders>

One thing to note about this solution is that it does require a parent-child relationship to be present in the actual xml data. Often, this relationship is implied in xml by the nesting structure. In such a case, this solution would not be able to find the proper context for the LineItem nodes that are to be rolled up. This example uses the PONumber node to define the parent-child relationship. Including this data in the source xml data in both the parent and child nodes allows that context to be defined so that all LineItems on the same PO can be aggregated.

Here is a screen-shot of the map I used:

How much of C# syntax can you use in an Expression shape?

In case you were wondering why you are not able to do certain operations in the BizTalk Expression shape within an Orchestration, here is the answer:

How much of C# syntax can you use in an Expression shape?

The bottom line is that the language under the hood is not C#, but is XLANG/S which cannot support certain tasty morsels that C# developers know and love (for, foreach, (++,–), etc. Big frowny face for that one!

Here’s another link that goes into even more detail:

http://geekswithblogs.net/cyoung/articles/3820.aspx

Using App.Config Settings in BizTalk 2004

This is an example of how to use app.config settings in a BizTalk 2004 Orchestration.
First, you must setup the configuration section that you want to use. This is an example of what this looks like:

<!–This is an example of setting up your configuration section–>
<configuation>
<configSections>
<section name=’MyConfigSection’ type=’System.Configuration.NameValueSectionHandler’ />
</configSections>

<MyConfigSection>
<add key=’MyKey’ value=’MyValue’ />
</MyConfigSection>
</configuration>

Then, create an orchestration variable of type System.Collections.Specialized.NameValueCollection (I’ll call it varConfigHandler)

This is an example of using the varConfigHandler in an expression shape:

// varConfigHandler has to be instantiated and bound to the config section we are interested in
// Notice that the result of the GetConfig function call is being cast into a NameValueCollection
varConfigHandler = new System.Collections.Specialized.NameValueCollection((
System.Collections.Specialized.NameValueCollection)
System.Configuration.ConfigurationSettings.GetConfig(“MyConfigSection”));

// Now, we can access keys in the config section
varConfigHandler.Get(“MyKey”);

For deployment, copy the configSections/section and MyConfigSection into the config file for the BizTalk Service which is located at:

C:\Program Files\Microsoft BizTalk Server 2004\BTSNTSvc.exe.config

I’m Back!

Okay, the new version of Blogger is waaaaay better than the previous version. In fact, I like it a lot better than MSN Spaces…so I’m coming back!

Over the next few weeks I’ll be going through the painful process of moving my posts over from my Spaces blog.

Yay Blogger! 🙂