Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Developing with the Microsoft Business Rule Engine

4.92/5 (18 votes)
5 Feb 2009CPOL18 min read 146.4K  
Explains practically working and developing with the Business Rule Engine (BRE).

Who is this article for?

This article is for two sets of readers: those who are new to Microsoft Business Rule Engine and are looking for a direct hands-on approach rather than going though the theory and architecture behind BRE (links for that at the Additional Resources section), and those who are familiar (but not experts) with the topic in general, and are looking to understand, in a practical manner, how to program with BRE and leverage some of its powerful offerings.

Introduction

All applications include business rules (at least they should). Modeling these business rules inside the application code directly adds several maintenance and operation challenges:

  1. Once you have your business rules “hard-coded” in your application, any future change for a certain rule will result in changing the code, compiling, and deploying. And since business rules do change in real life, this would quickly turn into a nightmare.
  2. You might think “configuration files”. Now, while this would be a perfectly acceptable technical solution, it is far from being ideal. Think about it: you start adding key-value pairs into configuration files and reading them in your code. As rules evolve, you will find yourself stuck with tens of entries distributed over multiple configuration files.
  3. Business rules are there to serve the business of the client to whom you are building the application for. As such, it is very convenient that the client is allowed to change business rules without going back to the technical team to change code or update configuration entries. After all, the GM of a local bank cannot understands why she cannot change the interest rate of loans without calling on for the geeky developer to do his…well, geeky stuff! As such, a dedicated business rule engine would be a perfect solution: Enter Microsoft Business Rules Engine (BRE).

Before getting you jumping of excitement, there is one huge problem currently in BRE: it is only available as a part of a BizTalk Server License. This means that in order to use it, you have to purchase BizTalk Server; not so nice I know. There are talks that Microsoft will make BRE a standalone product, but if that’s true or of it will happen anytime soon remains unclear.

So for the time being, you will most probably utilize BRE only in solutions which already has a BizTalk Server license as part of it.

Before delving into the topic, please take into account that while I intend to present the most of practical BRE development, there are a lot of details regarding its architecture and API which are not covered here. The Additional Resources section at the end of the article presents MSDN links that cover BRE.

Overview

To start with, the inner workings of BRE are based on the RETE algorithm; a highly efficient algorithm for implementing business rule engines. This article won’t touch on the topic, but if you are interested about how BRE is working internally, you should read the link shown on the Additional Resources section.

So, let’s start with some definitions in simple terms:

  • Business Rule Composer: This is the GUI that we will use to create, manage, and test policies. This is a client component that can connect to any BRE database (more on this later).
  • Policy: Policy is the unit of execution for rules. You create a policy and then you add rules to it. Later, you test and execute the policy, not the rules by their own.
  • Policy State: Policies can be at one of the following states:
    • Editable: after you create a policy, it is in the “Editable” state.
    • Saved: for saving your work, you can save the policy, but it is still open for editing; it’s like saving a file then opening it again for editing.
    • Published: when you publish the policy, it is read-only and cannot be edited again. It is not yet ready to be used, but closed for editing; a staging-like behavior.
    • Deployed: when you deploy a published policy, it is ready for usage from within applications. You can un-deploy a policy, but you cannot move it back to the editable state.
  • Policy Versioning: Once a policy is deployed, it gets a version and cannot be edited again. If you want to change the policy, you have to create a new version and deploy it. A single policy can have many versions deployed, and you can instruct your application to consume any version of that policy. You surely can think about how much this feature is helpful…
  • Rules: Rules are the actual representation of business rules inside a policy. These are similar to the notion of rules you know from traditional development. They contain data (facts), predicates, and actions.
  • Facts: Data that BRE operates against. You supply BRE your business data and – based on the defined policy and rules – actions are taken.
  • Fact Source: Fact sources can be XML documents, database, or .NET objects. XML is usually used when calling a policy from BizTalk Server, but it definitely can be used from code. Database is also a powerful fact source, but since most .NET applications deal with data as business objects, this article uses .NET objects as the fact source.
  • Functions:
    • Assert: is the act of “feeding” the BRE the fact to process.
    • Retract: is the opposite of assert; basically, removing a fact from the BRE memory.
    • Update: indicates that a certain fact should be reevaluated, causing all rules using the fact to be reevaluated.

Installation

To install BRE, run the BizTalk Server installation wizard. Uncheck all options except for the Business Rules Components option, as shown in the figure below:

1.JPG

The .NET Business Model

First, let’s create the .NET business data model. It’s the data that we are going to create our business rules against.

Create a C# class library called “BusinessModel”. Now, create a class “Applicant” as follows:

C#
namespace BusinessModel
{
    public class Applicant
    {
        int applicantAge;
        public int ApplicantAge
        {
            get { return applicantAge; }
            set { applicantAge = value; }
        }

        int projectLoanAmount;
        public int ProjectLoanAmount
        {
            get { return projectLoanAmount; }
            set { projectLoanAmount = value; }
        }

        int score;
        public int Score
        {
            get { return score; }
            set { score = value; }
        }

        string riskGrade;
        public string RiskGrade
        {
            get { return riskGrade; }
            set { riskGrade = value; }
        }

        string loanRisk;
        public string LoanRisk
        {
            get { return loanRisk; }
            set { loanRisk = value; }
        }

        int income;
        public int Income
        {
            get { return income; }
            set { income = value; }
        }

        bool isApproved = false;
        public bool IsApproved
        {
            get { return isApproved; }
            set { isApproved = value; }
        }
    }
}

The idea here is that in our sample program later, we will populate this business object with some data, pass it into the BRE, execute a policy, and take the result back without writing a single business rule inside our code. Hold on for that though, more fun stuff ahead to get the most of the topic.

Testing is a fundamental part of any development process; the BRE is no exception. This is especially important because in BRE, a policy can be called from an application only if it’s deployed. And since a deployed policy cannot be edited anymore, there is a necessity to make sure that the deployed policy is tested thoroughly. Later, we will create the policy and pass in the fact data (our “Applicant” business model). Thinking ahead, we need a way to test this before we actually deploy the policy and run our program. Testing BRE policies can be done in two methods:

  • Using the PolicyTester class
  • Using the Business Rule Composer and using a Fact Creator

This article uses the second approach for a single reason: examining the Output Trace. This will be clear later. So now, use the below code to create a class called “ApplicantFactCreator”:

C#
namespace BusinessModel
{
    public class ApplicantFactCreator : IFactCreator
    {
        public object[] CreateFacts(RuleSetInfo ruleSetInfo)
        {
            object[] facts = new object[1];

            Applicant applicant = new Applicant();
            applicant.ApplicantAge = 20;
            applicant.ProjectLoanAmount = 10000;
            applicant.Score = 150;
            applicant.Income = 10000;

            facts[0] = applicant;
            
            return facts;

        }

        public Type[] GetFactTypes(RuleSetInfo ruleSetInfo)
        {
            //not implemented
            return null;
        }
    }
}

The class implements the IFactCreator interface which makes it available for the Business Rule Composer testing wizard. The code is simple; it mainly creates the business object (the applicant), populates it with some data, and then returns that object. Now, the object (fact instance) will be available for testing, as will be demonstrated later.

The library has to be deployed into the GAC to be used by the BRE. Sign and build the library, then use the GacUtil command to deploy the DLL into the GAC.

Examining the Business Rule Composer

Open the Business Rule Composer as shown below:

2.JPG

You will now get the UI which we will examine as we go on. One thing to note initially is that you can select the server (database) to which the composer is connecting. The database name is “BizTalkRuleEngineDb”. The window shown below allows you to specify the database server name:

3.JPG

If you do not get the window at startup, you can do so by using the “Load…” option from the Rule Store menu.

Creating Your First Policy

Since the approach of the article is practical, let’s start creating the policies and we will explain the concepts as we go.

Create a new policy called “PriorityPolicy”. Automatically, you will get version 1 of this policy. Now, inside the version, we need to start creating rules. So, let’s roll:

Right click the version node and select “Add New Rule”. Keep the name of the created rule as “Rule1” for simplicity. Now that you have the rule, you need to start adding conditions and actions; however, you need your fact data for this. So, go to the Fact Explorer window, select the .NET classes tab, right click, and import the DLL from the GAC. Once you do so, you will see the classes we have created. Note that each property we created is represented by get and set accessors:

4.JPG

Now, go to the “IF” window, right click and select “Predicates GreaterThan”. In the resulting expression, drag the “get_Income” fact data into the argument1 placeholder and type “5000” into the argument2 placeholder. In the “THEN” window, drag the set_RiskGrade fact data into the “Actions” node. Type “RG2” into the set accessor. The final rule is shown below:

5.JPG

Just like that, you have defined your first business rule; you are saying that if the Income is greater than 5000, then set the RiskGrade to “RG2”.

Now, create a new rule – Rule2 – which sets the RiskGrade to “RG1” if Score is less than 200, as follows:

6.JPG

Understanding Priorities

Now, if you have done some critical thinking about what we have done so far, you should notice one important hint: the order at which rules will execute will actually change the final outcome.

If both Rules 1 and 2 will be evaluated to true, then RiskGrade will be set to RG1 or RG2 depending on which rule gets executed first. To solve such cases, you should always give your rules priorities.

Without priorities or with equal priorities, rules will be executed in a random order. Priorities are set by selecting each rule and setting its priority property in the Properties window. Higher priorities get executed first. In our current example, assume that we want Rule2 to execute first and Rule1 to come second. As such, give Rule2 priority 2 and Rule1 priority 1 using the Properties window.

Testing Your First Policy

Now, everything we have done so far will come together. First, save your policy by right clicking on the version node and selecting Save. Recall that this will still allow you to edit your policy. Now, right click again on the version node and select “Test Policy…”. From the window opened, we now need to select the Fact Creator which we will use for testing. Recall that previously, we have created a class called ApplicantFactCreator which implements “IFactCreator”. This class creates a fact (applicant) for the sole purpose of being used for testing by the composer. Also, recall that we have deployed the DLL (which contains the fact and the fact creator) into the GAC. Now, in the fact creator portion of the window, select the “Add…” button and select the DLL from the GAC; the fact creator will be detected (since it implements IFactCreator) and listed, select it. The final view will be as follows:

7.JPG

Finally, click “Test”. Before examining the output trace in the next section, let’s summarize where we are:

Pause, Recap, and Next

We are building an application which processes loan requests from applicants. Based on certain rules, we want to either accept or reject that loan request. While we’re on it, we want to calculate the risk grade. By using the BRE, we are escalating processing and evaluation of our business rules.

We have created our business object which represents the applicant. For testing our rules, we have also created a fact creator class whose sole purpose is to allow us to test our rules against a true applicant (fact instance) directly from the composer UI. This is important because if we deploy the policy and test it directly in our program, we won’t be able to edit it again. Next, we installed the business object DLL in the GAC because it needs to be consumed by the BRE.

We have created our first policy which contains two rules to calculate the risk grade. We need to specify the priorities of these rules because the order in which they execute will affect the outcome of the risk grade. We have chosen to say that we want Rule2 to be evaluated first.

From the composer, we loaded our fact creator and tested our policy against it. In the next section, we will examine the output trace which will make you understand more about the BRE.

Output Trace

The output trace below has been reformatted for displaying purposes; also, I have deleted the GUIDs that you will see in your output screen if you have been following the steps of this article.

(Read row by row from left to right)

8.JPG

  1. The applicant fact instance (BusinessModel.Applicant) has been asserted into the engine. Recall that assertion means that the fact is now available for processing by the BRE.
  2. The condition of Rule1 is evaluated. The left operand gets its value from the fact data and is evaluated against the right operand. In this case, the test result is true.
  3. Since the test result is true, Rule1 is added to the “Agenda”. Agenda is the set of operations that will be executed by the engine. The “Conflict Resolution Criteria” is the priority of the rule. It is important to note that the rule is not executed yet, it is just added to the agenda. Also note that it is added to the agenda regardless of the order it will be executed in. All rules will be evaluated, added to the agenda if the evaluation succeeds, and finally executed based on their priority. Just keep in mind that the order of rules evaluation and addition to the agenda is totally irrelevant.
  4. The condition of Rule2 is evaluated. The test yields true based on the value of the fact data.
  5. The agenda is updated and Rule2 is added to it. Its priority (Conflict Resolution Criteria) is 2.
  6. Now, the agenda is ready since there are no more rules to evaluate. As such, the rules should be executed. Since Rule2 has a higher priority (2), it is fired first.
  7. Rule1 is fired next.
  8. The fact instance is retracted. Recall that retraction means that the fact is no longer available for BRE processing.

From step 8, we should take note that the fact we are dealing with here is called “Short Term Fact”, meaning that it is available for BRE only for the life time of the application execution. “Long Term Facts” can be used in cases where fact data seldom changes; a technique called Fact Retriever is used just for that. This article will not discuss Long Term Facts but you can find a link at the Additional Resources section.

Understanding Fact Updates…and Output Trace again!

For this section, create a new policy “CompletePolicy”. You also need to create three rules: Rule1, Rule2, and Rule3 as shown below :

9.JPG

10.JPG

11.JPG

You should be familiar with creating rules now, just note the usage of the “AND” logical operator for combining conditions…

Give Rule1 priority 3, Rule2 priority 2, and Rule3 priority1. Now, try to analyze how these rules are working: if the conditions of Rule1 are evaluated to true, then IsApproved is set to true. Rule2 and Rule3 are affected by the outcome of Rule1 because their conditions will only succeed if IsApproved is set to true (recall from the Applicant class that IsApproved is set by default to false). As you can see in this example, it is critical to give rules priorities because we want Rule2 and Rule3 to get executed only after Rule1 does.

Well now, save the new policy and use the same procedure you used before to test this policy via the fact creator. Now to the fun part again; let’s examine the output trace!

12.JPG

  1. Fact is asserted into the BRE for processing.
  2. First condition for Rule1 is evaluated to true.
  3. Second condition for Rule1 is evaluated to true.
  4. Since all conditions for Rule1 are evaluated to true, Rule1 is added to the agenda.
  5. Rule2 is evaluated. Now, since Rule1 has been evaluated, you might be tempted to think that the evaluation of the Rule2 condition would yield true; after all, IsApproved should be set to true from Rule1, right? No, recall that Rule1 has been only evaluated, not executed; so IsApproved is still set to the default value false. So, what is the deal here? How do we tackle such a scenario? Keep reading…
  6. Rule3 is evaluated. Similarly to Rule2, this would be evaluated to false again because IsApproved is still false.
  7. Since Rule1 has been the only rule which was added to the agenda, it is fired. Oh, note that only at this time will IsApproved be set to true. You will see how we will benefit from this momentarily.
  8. Finally, the short term fact is retracted from the BRE memory so that it is no longer available for processing.

So now, we have a problem in our hands: we want Rule2 and Rule3 to “feel” the change of IsApproved caused by Rule1; however, by the time Rule1 is executed, Rule2 and Rule3 are already evaluated to false and consequently not added to the execution agenda.

The solution is easy: recall the “Update” function we defined previously. The Update function takes a fact as a parameter and causes all rules that use this fact to be re-evaluated. So, if we edit Rule1 action to include the update statement, this should solve the problem as follows:

13.JPG

Now, run the test again. Oops! Did you get an “out of memory exception”? That’s normal because if you carefully examine what’s going on now, you will conclude the following: after all the evaluation happens again, Rule1 will be the only one added to the agenda. So, it’s executed, and this time calls the “update” statement on the Applicant fact. As explained previously, this will retrigger the evaluation cycle again and causes the Applicant fact to be asserted with the updated IsApproved property set to true. Cool so far. So now, on the next evaluation cycle, all rules including 2 and 3 will be evaluated to true (because IsApproved is set to true), and as a result, the agenda will include Rules 1, 2, and 3. Next, Rule1 is executed. Can you guess what happens then? The update statement is executed again causing re-assertion and re-evaluation without Rules 2 and 3 being fired at all. Think about this and you will conclude that you got yourself a nice infinite loop going on! That’s why you are receiving the out of memory exception.

By the way, just in case you are tempted to think that you can solve this problem by adjusting priorities so that Rules 2 or 3 would fire first, think again. Setting aside the fact that this would basically damage your business rules, even if Rule3 is executed last, the update statement would still cause the re-assertion and re-evaluation to occur each and every time Rule1 is executed. So, in short, priorities have nothing to do with solving such problems.

The solution actually is simple, and it is a common programming sense: just code against it! The following figure shows Rule1 modified to solve the infinite loop problem:

14.JPG

You just have to add a condition to ensure that Rule1 does not get executed if IsApproved has been already set to true.

Now, go on to test the policy. Since by now you should be familiar with interpreting the output trace, I won’t list it here. But, its summary here is what is going on:

  1. The first evaluation cycle results is Rule1 being the only rule in the agenda.
  2. Rule1 is fired and IsApproved is set to true. The update function causes the Applicant fact to be re-asserted and all rules to be evaluated again (because they are using the Applicant fact, they would not be re-evaluated otherwise).
  3. In the next evaluation cycle, Rule2 is added to the agenda because IsApproved is set to true. However, Rule1 does not pass the evaluation this time because we have added a condition of testing IsApproved against being true. Rule3 fails the evaluation because of the score condition.
  4. Rule2 gets fired and execution ends.

Calling Policies from .NET…Only for Experienced Developers!

Just kidding…calling policies from .NET is just that: you declare the policy name you want to call, pass in the fact instance, and then execute. The below code shows just that:

C#
Applicant applicant = new Applicant();
applicant.ApplicantAge = 20;
applicant.ProjectLoanAmount = 10000;
applicant.Score = 150;
applicant.Income = 10000;

Policy policy = new Policy("CompletePolicy");
policy.Execute(applicant);

Calling Policies from BizTalk Server

Working with BRE does not differ on whether you intent to call policies from .NET or BizTalk. While this article won’t discuss it, basically you create your rules just like you did following this article. You test them the same way. Finally, when you’re ready, you place a CallRules shape inside your orchestration and configure it to call the required policy and pass in the fact source, which is usually your XML message.

Additional Resources

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)