Wednesday, January 31, 2007
Tuesday, January 23, 2007
BizTalk - Make the Port Operation Unique
This is a compelling article on why the operation property of an orchestration port should be uniquely named.
Monday, January 22, 2007
How to Debug a BizTalk Rules Policy in Code
I was experiencing an unreported error when I was trying to dynamically call a BizTalk 2006 rules policy in C# code. The policy ran and gave me no exception information and was annoying me to no end!
It turns out the issue had to do with facts and namespaces. I didn't discover this, however, until I learned from the link above how to put a debug trace into the policy so it would tell me what was going on.
Here's a snippet:
TypedXmlDocument typedResult = new TypedXmlDocument("ValidationResult", validationResult);
TypedXmlDocument typedTransaction = new TypedXmlDocument("Transaction", transaction);
System.Collections.ArrayList FactList = new System.Collections.ArrayList();
FactList.Add(typedResult);
FactList.Add(typedTransaction);
DebugTrackingInterceptor debug = new DebugTrackingInterceptor(@"D:\trace.txt");
Policy rulePolicy = new Policy("ValidateTransaction");
rulePolicy.Execute(FactList.ToArray());
rulePolicy.Dispose();
Wednesday, January 17, 2007
Passing an Array of Facts into the Rules Engine
The last piece of the puzzle for me in working with generic messages to do away with a TON of duplication is to dynamically call a rules policy to validate outbound EDI messages.
This is some sample code I found on the blog mentioned above:
//System.Collections.ArrayList List;
sCon = "Initial Catalog=Northwind;Data Source=(local);Integrated Security=SSPI;";
con = new System.Data.SqlClient.SqlConnection(sCon);
dcNorthwind = new Microsoft.RuleEngine.DataConnection("Northwind", "ShipperCountry", con);
List.Add(dcNorthwind);
xmlDocument = msgShippingRequest;
typedXmlDocument = new Microsoft.RuleEngine.TypedXmlDocument("RoleLinkSample.ShippingRequest",xmlDocument);
policy = new Microsoft.RuleEngine.Policy("ShippingPolicy");
List.Add(typedXmlDocument);
policy.Execute(List.ToArray());
msgOutgoingShippingRequest = typedXmlDocument.Document;
policy.Dispose();
typedXmlDocument = null;
dcNorthwind = null;
Tuesday, January 16, 2007
Dynamic Mapping (Transforms) in Biztalk Orchestrations
This is how we call maps dynamically in Biztalk 2006.
tMapType = System.Type.GetType("DynamicMaps.Map_A, DynamicMaps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=faed587cb93de4ea");
construct Out_Xml
{
transform (Out_Xml) = tMapType(In_Xml);
BizTalk Correlation of Untyped Messages
This is VERY cool...
I had two choices: created fifty separate orchestrations to handle all of the types and scenarios I needed to cover, or find a way to correlate untyped (XmlDocument) messages. I searched for the latter and found this article.
public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
{
string trackCode = Convert.ToString(System.Guid.NewGuid());
inmsg.Context.Promote("TrackingID", "http://Microsoft.Demo.Customer.CustomerPropertySchema", trackCode);
return inmsg;
}
Sweet!
Wednesday, January 10, 2007
MSDN TV: Introduction to Analysis Services 2005
MSDN TV: Introduction to Analysis Services 2005
SQL Server Query Performance Tuning
http://www.sql-server-performance.com/statistics_io_time.asp
Create Data Insert Scripts
http://vyaskn.tripod.com/code/generate_inserts.txt
How to remove xmlns attributes
Yippie friggin doo da! I found this xsl script that strips namespaces and prefixes out of an xml document. This is going to be very useful in some Biztalk 2004 transformations that I doing.
This is what I ended up with:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<!-- remove element prefix (if any) -->
<xsl:element name="{local-name()}">
<!-- process attributes -->
<xsl:for-each select="@*">
<!-- remove attribute prefix (if any) -->
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
BizTalk Server 2006 Business Rules and Static Methods
http://blogs.msdn.com/richardbpi/archive/2005/11/14/492489.aspx
Windows Group and User Accounts in BizTalk Server 2006
Windows Group and User Accounts in BizTalk Server 2006
Biztalk 2006 Suspended Message Routing and Error Reporting
Biztalk 2006 Suspended Message Routing and Error Reporting
Biztalk Send Port File Name Macros
%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
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?
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
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!
Over the next few weeks I'll be going through the painful process of moving my posts over from my Spaces blog.
Yay Blogger! :)