Tag Archive: BizTalk Server 2010


About a month ago I announced the release of the beta installer for the BRE Pipeline Framework. Since then I have been hard at work trying to get the framework up to scratch with the goal towards starting to use it on new projects, and at the very least to use it to make POCs a lot quicker. I have added a lot of new features to make the framework easier to use, and made a lot of tweaks.

For those who don’t know too much about the framework, the whole idea is to remove the need to constantly write variations of pipeline components used to manipulate a message’s context or body along with all the deployment issues and complications that arise when changes are required for these components, and to instead use a pipeline component that allows for message manipulation logic to be encapsulated into the BRE (Business Rules Engine) so that developers are concentrating on logic instead of plumbing.  Further details on the framework are listed on the codeplex project page, and documentation detailing installation and usage is included on the codeplex project documentation page.

Details on some of the changes below (there are many more but these are the most important ones).

  • The BREPipelineFramework.SampleInstructions.ContextInstructions vocabulary now contains definitions that allow you to get and set context properties from all the out of the box BizTalk and EDI context properties through enumeration (ie. you don’t need to know the name/schema namespace of the properties, you just need to know the .NET namespace such as BTS, EDI, or SQL and can select the property from a drop down list).
  • The set type vocabulary definitions in the BREPipelineFramework.SampleInstructions.ContextInstructions vocabulary will now allow you to set values to context properties of any type (parameter on the method changed from string to object), not just strings.
  • It is now not possible to manipulate a BizTalk message in a rule condition, only in an action which makes the framework a whole lot safer to use.  Any errors encountered in the pipeline will result in the original message/context being reverted to.
  • The pipeline component now uses VirtualStream instead of MemoryStream to improve memory management for large messages.
  • Fixed bug in the GetXPathResult definition in the BREPipelineFramework.SampleInstructions.HelperInstructions whereby the stream position wasn’t being reset and thus the message body was being lost.
  • Added BizUnit based pipeline unit tests to provide a pretty good level of coverage for functions provided by the framework.
  • Comments made throughout the source code to make it easier to understand for those who want to dive into it and extend the framework with their own MetaInstructions and Instructions.

I encourage you to try to make use of the framework and give me any feedback that you have.  My belief is that the learning curve should not be too steep and that it has the potential to increase productivity and allow developers to turn around solutions a lot faster but I need the community’s help to qualify this.  Please post any feedback to the codeplex project discussion page.

To pique your interest, below is a screenshot of one of the rules used in the framework unit tests to give you an idea of what is possible with the framework (note the use of enumerations to choose the relevant context properties).

ExampleRule

Advertisements

We recently hit a problem on a project we were working on whereby we were forced to switch from using a native BizTalk database adapter to using the third-party Community Adapter for ODBC which has been refactored for BizTalk 2010 by TwoConnect.  Having access to the source code allowed us the opportunity to work around a very specific issue while we hold out the hope that Microsoft applies a permanent fix to the problem for us (I’ll blog about this issue soon).

This has been my first time dealing with the ODBC adapter for BizTalk and I must say that while it is far from perfect and does have limitations (just search for “TODO” in the source code and you’ll see that there is much room for improvement), it has far exceeded my initial expectations and contains a rather elegant project structure which enabled me to make my required changes in a structured and sensible manner, and also contains some pretty competitive schema generation and testing tools.

One major issue I had however was that when I deployed the adapter using the MSI that was built using the install project that is included with the source code, I started seeing the below error every time I tried to start my receive locations.

The Messaging Engine failed to create the receive adapter “ODBC”.
InboundAssemblyPath: “NULL”
InboundTypeName: “Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCReceiveAdapter.ODBCReceiver, ODBCAdapterManagement, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL”
Exception Details: “Could not load type ‘Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCReceiveAdapter.ODBCReceiver’ from assembly ‘ODBCAdapterManagement, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL’.”

Luckily I managed to find a comment on a blog post (thank god for the blogosphere) which pointed me in the right direction.  A user called “dawa” pointed out that the wrong InboundTypeName and OutboundTypeName were registered in the dbo.adm_Adapter table in the BizTalkManagementDb database.  The InboundTypeName really should be “Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCReceiveAdapter.ODBCReceiver, ODBCReceiveAdapter, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL” but is instead “Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCReceiveAdapter.ODBCReceiver, ODBCAdapterManagement, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL”.  Likewise the OutboundTypeName should be “Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCTransmitAdapter.ODBCTransmitter, ODBCTransmitAdapter, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL” but is instead “Microsoft.BizTalk.Adapters.ODBC.RunTime.ODBCTransmitAdapter.ODBCTransmitter, ODBCAdapterManagement, Version=1.2.0.0, Culture=neutral, PublicKeyToken=0ad1f077efbaab97, processorArchitecture=MSIL”.

Now I’m as brave as the next person, but making changes to the BizTalk management databases on non-development environments should really be a no-no.  If you actually open the Install.vdproj file using a text editor you’ll find that you can adjust the InboundTypeName and OutboundTypeName.  Once this is done you can rebuild the MSI and it will now behave correctly.  If however you have already previously installed the ODBC Adapter and have encountered this error, you will need to uninstall the ODBC Adapter from the Add/Remove Programs control panel and you will also need to deregister the ODBC Adapter in the BizTalk Administration Console by browsing to Adapters within Platform Settings and deleting the ODBC Adapter (you will need to ensure that all receive locations and send ports that use the ODBC adapter have been deleted first).  You can then install the fixed MSI and register the ODBC adapter again, and it will now contain the correct InboundTypeName and OutboundTypeName.

I don’t believe that this problem exists with the MSI file that is published on TwoConnects website as I didn’t face this problem until I installed an MSI built from the source code (I had originally just installed the MSI from their website).  It only appears to be in the source code that they have published.  I will advise them of this problem but thought I better document it before it catches the next unwary developer off guard.

The BizTalk business rules engine is a very powerful asset in an integration specialist’s toolbox, but it can be tricky when you are trying to operate with complex schemas as you try to get your head around how it works.  The problem I will try to illustrate here is that the default schema vocabulary definitions will not allow you to spread your conditions and actions across different records in an XML schema if they are both contained within a single instance of a repeating reccord.

For this example, lets say that we want to evaluate a batch of customers, and for those that are Infinity years old (ok, not the best example) we are going to set their AwardsLevel to Gold.  We are going to deal with the below XML schema (note that RegularCustomer is unbounded and that Age is optional) and XML input file.

Of course Chuck Norris is Infinity!

Let’s add some vocabulary definitions to get a customer’s age and to set his awards level.  To start with let’s just select the elements we’re interested in from the schema view when creating the definitions.

It’s now time to define our policy as below.

We need to test out the policy we’ve just created so lets save it first.  Now right click on the policy version and choose test policy.  You will need to click on the schema listed under XML Documents and click the add instance button, choosing to point towards our test XML file.

When you execute the test you’ll see that the results are somewhat unexpected, with every customer being awarded gold status, even the ones younger than Infinity.

It’s time to do some investigation.  I’ve found the quickest way to understand what makes a policy tick is to export it using the Business Rules Engine Deployment Wizard (you’ll find the shortcut under your start menu in the BizTalk Server folder) and to inspect the XML file.

The first thing of note is that in the bindings section there are two XML document instances, each with their own XPath selectors defined.  The first selector is to the CustomerDetails record and the second to the AwardsDetails record.  When you dig into the GoldAwards rule you’ll notice that the xmldocumentmember element in the <if> record has a xmldocumentref attribute of xml_31 (the CustomerDetails selector) and the xmldocumentmember element in the <then> record has a xmldocumentref attribute of xml_32 (the AwardsDetails record).  It’s pretty obvious that our if and then statement are not scoped to the same RegularCustomer record, and the path to enforcing the scope is to ensure that the xpath statements executed in the <if> and <then> records of our rule use the same selector.

So this time lets revisit our vocabulary definitions.  By default the XML selector and XML field values for GetAge and SetAwardsLevel look like the below.

GetAge Selector – /*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”]/*[local-name()=’CustomerDetails’ and namespace-uri()=”]

GetAge Field – *[local-name()=’Age’ and namespace-uri()=”]

SetAwardsLevel Selector – /*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”]/*[local-name()=’AwardsDetails’ and namespace-uri()=”]

SetAwardsLevel Field – *[local-name()=’AwardsLevel’ and namespace-uri()=”]

Let’s edit the definitions (you’ll need to make a new vocabulary version) so that the  XML selector and XML field values for GetAge and SetAwardsLevel look like the below (note that the selectors are now the same being scoped to the RegularCustomer record while the field digs into the required child records).

GetAge Selector – /*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”]

GetAge Field – *[local-name()=’CustomerDetails’ and namespace-uri()=”]/*[local-name()=’Age’ and namespace-uri()=”]

SetAwardsLevel Selector – /*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”]

SetAwardsLevel Field – *[local-name()=’AwardsDetails’ and namespace-uri()=”]/*[local-name()=’AwardsLevel’ and namespace-uri()=”]

Let’s change the rule such that it uses the new version of our vocabulary (note that a rule refers to a specific version of a vocabulary definition, it will not automatically choose the latest version of the vocabulary).  If we give it a test now we’ll see that only the RegularCustomer records with an Age of Infinity have Gold status :).  You’ll also see if you export the policy to XML that there is now only one xmldocument record in the bindings section and that both the <if> and the <then> use this instance.

Chuck Norris = Gold

There is one last important thing to note.  What if we passed in a RegularCustomer record which for whatever reason did not contain an Age element at all (remember that it is defined as an optional element)?  You’ll see that the rule fails and throws an exception, and thus the entire policy fails to execute with the below error.

RuleEngineRuntimeFieldNotDefinedException –  Field “*[local-name()=’CustomerDetails’ and namespace-uri()=”]/*[local-name()=’Age’ and namespace-uri()=”]” does not exist in XML document “BusinessRulesTest.BusinessRuleSchema”, selector “/*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”]”.

This here blog post (I suggest that everyone that intends to use the BRE read this as you will undoubtedly hit this problem sooner or later) suggests that we can get around this problem by editing the selector to ensure that the relevant fields exist.  In this case we need to ensure that the Age and AwardsLevel (might as well cater for this too) fields exist.  We can do this by editing the xpath selectors to look like below.

/*[local-name()=’RegularCustomers’ and namespace-uri()=’http://BusinessRulesTest.BusinessRuleSchema’%5D/*%5Blocal-name()=’RegularCustomer&#8217; and namespace-uri()=”][CustomerDetails/Age and AwardsDetails/AwardsLevel]

The catch here is that if you don’t update the xpath selectors for both the GetAge and SetAwardsLevel definitions then you’ll find that you are back to square one with all the RegularCustomer records getting a Gold AwardsLevel.  Thus one can conclude that the xpath selectors must match exactly when attempting to define a scope between rule actions and conditions.

%d bloggers like this: