Archive | BRE RSS for this section

Twin Cities Code Camp #15: Using the Microsoft BRE Code is now available

You can download the code I demonstrated in my Twin Cities Code Camp Session here:  http://sdrv.ms/Ht02Lm.  The slide deck is available here: TCCC_15_UsingTheBRE

Advertisements

See you at Twin Cities Code Camp #15!

As autumn is upon is, it reminds me that it is once again time for Twin Cities Code Camp on October 19th, 2013. This time I’ll be doing a presentation on using BizTalk’s rules engine from .NET clients.  There are a couple of articles on this site regarding the topic, but this presentation will be updated to use Visual Studio 2012 and BizTalk 2013.  We’ll also have an overview of BizTalk and the Business Rules Engine (BRE) for the uninitiated in BizTalk lore.  You’ll find an overview here.

I’ll be presenting my talk at 2:15 PM on October 19 in Room 56 in Rapson Hall on the University of Minnesota campus in the Twin Cities.  Code Camp always features a number of great sessions.  You may register here.  And as always, it’s 100% FREE!

Generating a BizTalk BRE Rule in .NET

As you can probably tell from other articles on this blog, I spend a great deal of time working with the Business Rules Engine (BRE) in BizTalk. Personally, I consider the BRE one of those great, often-overlooked technologies that should be used more than it is, but I digress…

One little-known thing you can do with the BRE is create rules on the fly in .NET. For this article, I’m going to provide a walk-through of how to do this.

First, though, you should spend a little time getting to understand the BRE if you don’t already. A great first step is to do the Virtual Lab provided by Microsoft.

The main artifacts in the BRE are Policies, Rules, Conditions, and Actions.  At a very high level, we can describe a Policy as a container for a number of Rules. Each Rule contains a set of Conditions, which if true, will cause Actions to be performed. A Condition can use any number of Predicates such as EQUALS or GREATER THAN and Operators like AND and OR to evaluate the state of Objects, XML Documents, etc. An Action can be an assignment, a method call, or something performed on the same or other objects that may be sent in at execution time.

Before writing your Rule-Generating code, understand exactly what it is you wish to accomplish with the rule. What will the input be? XML Document? Business Object? What conditions will need to exist for a rule to fire? What will happen when the rule does fire?

Once you know which type of rule or set of rules you’d like to generate, it’s usually a good idea to map it out a bit first. For our purposes, we’re going to update an ESB Toolkit BRE Resolver object to set Routing information for a message along an itinerary. If you’d like to learn more about using the ESB Toolkit, I have a an introductory article on this blog or you may also check out the excellent videos by Peter Kelcey. However it is not important for understanding the main points of this article.

Our policy will contain only a single rule. Here’s the logic:

IF
    The incoming messages is of type "http://myMessageTypeNamespace#messageRoot"
AND
    The ReceivePortName is "InboundMessagePort"
THEN
    Set TransportLocation to: "C:\FileDrops\FileOut"
    Set TransportType to:  "FILE"

First, reference the Rules Engine assemblies Microsoft.RuleEngine and Microsoft.Biztalk.RuleEngineExtension both located at <Program Files>\Common Files\Microsoft BizTalk\. Because we’ll also be accessing members of the Microsoft.Practices.ESB.Resolver.Resolution class for our conditions and actions, we’ll need a reference to Microsoft.Practices.ESB.Resolver as well.  If you have the ESB Toolkit 2.1 installed, it is located at <Program Files>\Microsoft BizTalk ESB Toolkit 2.1\Bin\ .  You may use classes in any .NET assembly here, just remember to alter your code and references accordingly.

After adding your references, add the following using statements:

using Microsoft.BizTalk.RuleEngineExtensions;
using Microsoft.RuleEngine;

Before we can construct our conditions, we need to access the artifacts that will be used. For our example, it will be the Resolution class mentioned above. Here is the code we need to create a binding to this class so we can use it in our Business Rule:

ClassBinding resolutionClassBinding = new ClassBinding(
    typeof(Microsoft.Practices.ESB.Resolver.Resolution));

We’ll also need to create bindings for the class members we want to evaluate. Note the names we use for the properties we’ll be evaluating. You’ll see names like “get_MessageType” instead of “MessageType” and “get_ReceivePortNameField” instead of “ReceivePortName”. At lower levels in the BRE code, properties are often accessed by calling a get_ or set_ function. An easy way to look up the names the engine will use is to load the class in the Facts Explorer in the Business Rules Composer and look at their names there.

Create instances of ClassMemberBinding, passing in the name of the members and the ClassBinding we created previously:

ClassMemberBinding messageTypeBinding = new ClassMemberBinding(
    "get_MessageType", resolutionClassBinding);
ClassMemberBinding receivePortNameBinding = new ClassMemberBinding(
    "get_ReceivePortNameField", resolutionClassBinding);

Next, we construct the logic. First, we’ll build each IF condition individually, add them to a collection of Logical Expressions, and ensure that we evaluate them all together with an And.

LogicalExpression messageTypeCondition = new Equal(
    new UserFunction(messageTypeBinding),
    new Constant("http://myMessageTypeNamespace#messageRoot"));
LogicalExpression portNameCondition = new Equal(
    new UserFunction(receivePortNameBinding),
    new Constant("InboundMessagePort"));

LogicalExpressionCollection conditionsList =
    new LogicalExpressionCollection();
conditionsList.Add(messageTypeCondition);
conditionsList.Add(portNameCondition);
LogicalAnd allConditions = new LogicalAnd(conditionsList);

After creation of the conditions, the next step will be to build out the actions that will take place should the conditions evaluate to true. For our purposes, the output values will be hard-coded, but you can use other rule inputs or method calls to create your values. First we start with an ActionCollection:

ActionCollection actions = new ActionCollection();

Now to add the actions to the list, we’ll need to create bindings to the class and its members that we’re interested in. As before, it’ll be references to members of the Resolution class. We’ll start by creating an ArgumentCollection and the ClassMember objects needed to bind to our list of Actions for the transport location method:

ArgumentCollection locationArgs = new ArgumentCollection();
locationArgs.Add(new Constant(@"C:\FileDrops\FileOut"));
ClassMemberBinding locationBinding = new ClassMemberBinding(
    "set_TransportLocation", resolutionClassBinding, locationArgs);

Next, the we’ll bind the Transport Type:

ArgumentCollection transportArgs = new ArgumentCollection();
transportArgs.Add(new Constant("FILE"));
ClassMemberBinding transportBinding = new ClassMemberBinding(
    "set_TransportType", resolutionClassBinding, transportArgs);

Now, add them to our Actions collection:

actions.Add(new UserFunction(locationBinding));
actions.Add(new UserFunction(transportTypeBinding));

Then we’ll create a Rule, add all the conditions and actions to it, and assign it to a new policy (or RuleSet). Our policy will use the default version, but you can also use the VersionInfo class to add your own major and minor version assignments.

Rule sampleRule = new rule("sampleRule", allConditions, actions);
RuleSet newPolicy = new RuleSet("samplePolicy");
newPolicy.Rules.Add(sampleRule);

Finally, we’ll need to deploy it to the BRE. We’ll need to publish and deploy the rule in two separate steps:

Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver
    deploymentDriver =
    new Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver();
RuleStore breStore = deploymentDriver.GetRuleStore();

// add the RuleSet to the database and publish it
try
{
    breStore.Add(newPolicy, true);
}
catch (RuleStoreRuleSetAlreadyPublishedException)
{
    Console.WriteLine("Warning: Ruleset \"" +
        newPolicy.Name +
        "\" is already published");
	return;
}

// now deploy the ruleset
try
{
    deploymentDriver.Deploy(new RuleSetInfo(newPolicy.Name,
        newPolicy.CurrentVersion.MajorRevision,
        newPolicy.CurrentVersion.MinorRevision));
}
catch (RuleEngineDeploymentAlreadyDeployedException)
{
    Console.WriteLine("Warning: Ruleset \"" +
        newPolicy.Name +
        "\" is already deployed");
	return;
}

And that’s it. 

Here’s an image of the policy we generated in the Business Rules Composer:

 Of course, there’s much more you can do than what is shown here but hopefully this sample code can help you get started.  In a future post, we’ll discuss how to use a regular XML message in your rules instead of just method calls in .NET assemblies as shown here.

Calling the BRE Remotely from .NET

It seems that the BRE comes up a lot in my work with BizTalk these days, which is why I’ve written some articles about its use.  In one article, I discuss how to call the BRE from .NET. The code in this article works very well when calling a local BRE installation, but we don’t address how to achieve the same goal when calling an instance of the BRE on another server. One ETM reader asked me about this (thanks, Gautam) which led me to an MSDN post by K Kanavia:

http://social.msdn.microsoft.com/Forums/en/biztalkgeneral/thread/373dd01a-2140-4327-affe-27a33ad77976

The approach in the article is to more explicitly use pieces of the BRE API that identify the server rather than to accept the default which is to use the local engine. This approach is detailed in the code below:

//Get the SqlRuleStore from BizTalkRuleEngineDb database on remote m/c
//pass on correct databaseserver and database name
Microsoft.RuleEngine.RuleSetDeploymentDriver rdd =
    new RuleSetDeploymentDriver(RuleEngineDatabaseServer,
    RuleEngineDbName);

SqlRuleStore sqlRuleStore =
    (SqlRuleStore) rdd.GetRuleStore();

//Get access to RuleSetInfoCollection for the policy,
//pass on the policy name
RuleSetInfoCollection rsic = sqlRuleStore.GetRuleSets(policyName,
    RuleStore.Filter.All);

//Create a RuleSet object based on the rule set information
RuleSet rs = sqlRuleStore.GetRuleSet(rsic[0]);

//Create the RuleEngine object
engine = new RuleEngine(rs);

//create facts here
Object[] facts = ;

//if u want to catch the debug info from MS BRE execution.
if (debugRulesEngine)
{
    dti = new DebugTrackingInterceptor(@rulesEngineDebugFile);
    engine.TrackingInterceptor = dti;
}

//Execute the policy by invoking RuleEngine.Execute method,
//passing on the facts
engine.Execute(facts);

Thanks to K Kanavia for posting this code on MSDN!

Inserting Nodes into a Message from the BRE

One of the problems I’ve always had with the Business Rules Engine (BRE) is that there is no built-in vocabulary or functionality that allows one to add nodes to an XML Document. Below I present one possible way to accomplish this.

Since the BRE doesn’t contain this capability natively, you have to rely on good ol’ .NET code to get the job done. For this, I’ve used basic DOM capabilities to do a node insertion–nothing fancy at all.

First, we have to work with the BRE’s own version of the XmlDocument called a TypedXmlDocument. The TypedXmlDocument is similar to the DOM version except, among other things, it carries a textual key which allows the BRE to identify it within a fact list. In the code below, we accept one of these as the parameter of our static method. The other parameters are strings which contain the xPath to the parent node, the name of the node we want to add, its value, and the namespace to use. Here’s the code:


public static void AddNodeWithValue(TypedXmlDocument txd,
    string parentNodeXPath, string nodeName,
    string nodeValue, string nodeNamespace)
{
    XmlNode node = null;
    XmlNode parent = txd.Document;
    node = parent.SelectSingleNode( xPath, txd.NamespaceManager );

    XmlDocument root = node.OwnerDocument;
    if (null == root)
    {
        root = txd.Document as XmlDocument;
        if (null == root)
        {
            return;
        }
    }

    // create a new node and add it in
    XmlElement newNode;
    if (nodeNamespace != null)
    {
        string prefix = node.GetPrefixOfNamespace(nodeNamespace);

        if (prefix == null || prefix == string.Empty)
            newNode = root.CreateElement(nodeName, nodeNamespace);
        else
            newNode = root.CreateElement(prefix, nodeName, nodeNamespace);
    }
    else
    {
        newNode = root.CreateElement(nodeName);
    }

    newNode.InnerText = nodeValue;
    node.InsertBefore(newNode, null);
}

Nothing fancy here, just simple DOM activity. Once you’ve built this code into a component, you can use it in a rule by adding it as a fact in the Business Rules Composer.

%d bloggers like this: