Tag Archive: BizTalk BRE

As much as I am a fan of the Business Rules Engine (BRE) shipped with BizTalk Server there are times when I think it could use some more love from our friends at Microsoft, and never have I felt this more so than during a recent deployment exercise. I was aiming to deploy BRE Policies that made use of .NET based facts via BizTalk MSIs and I ran into the below problems which have made me somewhat rethink how I will carry out deployments of BRE Policies in the future.

  • You can’t import MSIs containing BRE Policies which make use of .NET classes into the BizTalk Runtime until those .NET assemblies have already been added to the GAC. This is fair enough, but once you’ve added the assemblies to the GAC you need to remember to close and reopen the BizTalk Administration console before you try to import the MSI or the MMC will not acknowledge that the assemblies are now in the GAC as seen in the below screenshot. This isn’t the biggest of deals, but it does push you towards installing your MSIs before importing them, which is the opposite of what I’ve seen most BizTalk administrators do.
Unable to locate assembly

Could not load file or assembly or one of its dependencies

  • The second problem which is much worse is that if the fully qualified namespace of the .NET classes that are used to make up your BRE Vocabularies are long enough then you will encounter the below error during installation of the MSI claiming that the fully qualified file name must be less that 260 characters. This is fair enough, however the problem is exacerbated by the fact that BizTalk creates a very complex folder structure when it writes the vocabularies to the file system during the MSI install process. I don’t think that breaking away from namespace standards at an organization to hack your MSI deployments as an acceptable solution so I needed to find another deployment pattern.

File path too long

I like that MSIs make our lives a lot easier during BizTalk deployments and I didn’t want to veer far away from this model when working out a solution. What I decided was that I was going to export my BRE Policies into a separate MSI, containing only the BRE policies and nothing else. I then added that MSI as a resource in my BizTalk application. Next up I created a .cmd file which calls on the BTSTask command during MSI installs (see my previous post regarding selectively executing pre/post processing scripts if you want to understand how this works) to deploy the BRE Policies MSI into the BizTalk Runtime, the contents of the file being as below.

If “%BTAD_InstallMode%” == “Install” If “%BTAD_ChangeRequestAction%” == “Update” (“C:\Program Files (x86)\Microsoft BizTalk Server 2010\BTSTask.exe” ImportApp /Package:”C:\Program Files (x86)\Generated by BizTalk\AppName\AppName BRE.msi” /ApplicationName:AppName /Overwrite)

I then added this .cmd file as a resource in BizTalk as well and the file type to Post Processing. An MSI can now be exported for the entire application excluding the BRE Policies which are contained within the BRE Policy only MSI.

The deployment flow is now as below.

  • The deployer installs the full-application MSI file first (if this is not a first time release then they might want to stop the application using the BizTalk Administration Console or through some other means, depending whether there are orchestrations involved or not / whether they are overwriting existing policy versions).
    • First the .NET assemblies are added to the GAC by the MSI install, including any within the contained application which the BRE Policy might be dependant on.
    • The post processing script will then deploy the BRE Policy only MSI to the BizTalk runtime, thus publishing the BRE Policies. Because we are not doing this via the BizTalk Administration console there is no need to close and reopen the MMC. Since the BRE Policies are not contained within the full-application MSI but only in the BRE policy MSI (which is only being imported, not installed), we will not run into the greater than 260 character issue.
  • The deployer then imports the full-application MSI through the BizTalk Administration console, restarts host instances, starts the application etc….

This isn’t the only way to work around these problems, but it does work. If anyone has any other ideas please do let me know.

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: