Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / C#
Article

Getting Started with Drools.NET

Rate me:
Please Sign up or sign in to vote.
4.92/5 (19 votes)
23 Sep 2008CPOL15 min read 205.5K   5.6K   53   52
How to use Drools.NET in Visual Studio 2008

Introduction

This Article discusses using the Drools.NET project in a Visual Studio 2008 Forms application. It goes through the steps to use the project files, as well as a brief discussion of the Drools Rule Syntax.

Background

There are lots of articles on the web explaining why applications should have the business logic separated from the application logic. During my time with Blaze Software, Neuron Data, and Inference Corporation, I must have given that presentation a thousand times. I am not going to go into all of that here.

Instead, I will attempt to show what can be done with a Business Rules System -- even if you don’t have the six figure budgets necessary to implement a solution using one of the commercial Rules Management systems, (such as Fair Isaac’s Blaze Advisor or Ilog’s JRules -- or whatever it will be called now they have been bought by IBM.)

JBOSS Rules and DROOLS.NET

An open source alternative to the commercial business rules engines is DROOLS -- re-branded now as JBOSS rules. It has recently given its internal algorithm a speed boost, and it has announced that it will support the legacy syntax used by Inference’s ART, and NASA’s CLIPS project. (as well as several other rules syntaxes.) Honestly, I was never overly fond of the ART syntax – which was too LISP-like for my tastes. However, there is no arguing that it was a very successful language, and there are a lot of examples and applications still available today for it. The main thing is that with DROOLS, you have several syntaxes to choose from, which is always a good thing.

So, learning JBOSS Rules opens the doors to new techniques and architectures, but where to start?

Well, if C# and .NET is your cup of tea, you might want to have a look at DROOLS.NET.

Before We Start...

You can find the project page here:

http://droolsdotnet.codehaus.org/

Just don’t go there looking for documentation on how to use it if you are just getting started! It’s not available. Oh, they have a “Getting Started in Drools.NET” guide – but it is, strangely, full of typos. Whoever wrote it did NOT put it in Visual Studio and then cut and paste it to the document… apparently, they typed it from memory and then never bothered to test it… weird… (since that seems harder to me than putting it into Visual Studio to begin with.)

Happily, we have other sources of information available.

Scott McMaster has a nice article on his BLOG describing a very simple example to get you started:

http://softwaredevscott.spaces.live.com/blog/cns!1A9E939F7373F3B7!621.entry

This article is my somewhat expanded example which describes how rules are used in DROOLS.NET, and gives some explanation on how a rules system is implemented. It is not meant to be an in-depth tutorial, it is simply a starting point that I hope is useful.

I have the full application as part of the article so you can download and follow along – that way you don’t end up with typos like the unfortunate Drools.Net developer who did the online docs ;-) I should note that I am using the commercial version of Visual Studio 2008. I have not tested this in the free version, nor in 2005 – and, though I would expect it to run there, YMMV.

Using the code

I started with a simple Windows Form example that, when compiled, looks like this (when running):

DroolsApp.jpg

SETUP For Drools.NET

To start,

From there, we add 5 DLLS to your project:

"IKVM.Runtime.dll"

"IKVM.GNU.Classpath.dll"

"drools-3.0.dll"

"drools.dotnet.dll"

"drools-dep.dll"

Image 2

The IKVM dlls allow you to run a Java engine in a .NET world. It was a “quick and dirty” way for the developers of Drools.Net to get the code over. There are rumors of a native port in the works which would do away with these emulators, and make Drools.Net faster, but today, this is what we have.

Of course, you will also need the appropriate include statements in your project:

using org.drools.dotnet.compiler;



using org.drools.dotnet.rule;



using org.drools.dotnet;

The next step is to add the rules base to your project, and set its BUILD ACTION to "Ebedded Resource".

In reality, you probably shouldn’t do it that way. Having a rules file embedded in your project sort of defeats the whole purpose of having a rules file to begin with. The real power of a rules system is being able to change the rules without requiring a recompile of the project.

However, when I tried to load an external rules file, I got some exceptions thrown. I am not sure why yet, and haven’t been able to track it down. For now, I am going to post this article with the embedded rules file, and hope to update it later.

At least I KNOW this way WILL WORK. (UPDATE: This is now fixed... see notes at bottom of article...)

The rules file, called SimpleRules.DRL is in the attached package, but it only contains 3 rules.

They look like this:

package MinimalDroolsForm
rule "LargeOrder"
when
custOrder : CustomerRecord( Value > 50 )
then
MinimalDroolsForm.Form1.debugResult("Large Order");
end
rule "OldCustomer"
when
cust : CustomerRecord( DaysSinceLastOrder > 90, Status != "Re-Active" )
then
MinimalDroolsForm.Form1.debugResult("Old Customer");
cust.Status = "Re-Active";
modify(cust);
end
rule "Discount"
when
Disc : CustomerRecord( Value > 50, Status == "Re-Active" )
then
MinimalDroolsForm.Form1.debugResult("Offer WELCOME BACK discount" );
end

A quick breakdown of the rules file is as follows:

The first line simply tells Drools that it is referring to the “package” MinimalDroolsForm, which is my Windows Form namespace.

Besides the Window Forms code, this namespace also includes a small “Customer Object” that looks like this:

public class CustomerRecord
   {

        public String Id { get; set; }
        public String Status { get; set; }
        public int Value { get; set; }
        public int DaysSinceLastOrder { get; set; }
        public int TotalOrders { get; set; }
        public CustomerRecord()
        {

        }
    }

In the rest of the rules file, I have 3 rules named “LargeOrder” , “OldCustomer “, and “Discount” .

A rule is broken down into 2 parts. You will often hear this referred to the Left Hand Side of the Rule and the Right Hand Side. Or, LHS and RHS. This is old terminology that has followed rules systems since their early PROLOG days. In Prolog, rules were expressed just that way, with the conditions placed on the left hand side, and the actions placed on the right hand side.

Even though we now “stack” rules so the condition is on TOP of the action, I still find myself referring to the rule’s LHS and RHS – old habits are hard to break -- so I ask the reader’s indulgence.

Anyway, the LHS of the rule, or the condition, specifies all of the things a rule must look at before it takes its specified action (sometimes referred to as the rule “Firing”.)

Usually, these conditions will form something known as “patterns”. There are many ways to think of a pattern. I find most programmers have some experience with databases, so the best way to think of a pattern is that it is similar to an SQL selection set.

Understand that there may be hundreds, or even thousands, of objects in a rule system. By establishing patterns, we can filter out all of the non-essential data, so the rule can look at the areas of data that are relevant to it, and act upon only that.

Since we may re-use a pattern within a rule, we “name” it. This is done by putting the name to the left of the pattern. So, in the first rule, we see this:

custOrder : CustomerRecord( Value > 50 )

custOrder is the name of the pattern. (We could have called it anything - Fred if we wanted - but helpful names make the rule easier to understand.)

CustomerRecord is the name of the class about which the rules are written. The comparisons in the parenthesis are the conditions which selects the CustomerRecord objects which are relevant. In this case, the property “Value” is the qty ordered. If they order more than fifty items, we consider that a large order.

This is all that is on the LHS of the rule. Next, we come to the action, or the RHS.

We don’t do anything fancy here. We simply print out that the rules finds the order large.

The next rule is more interesting.

Here, we not only print the result, we also change its status to “Re-Activate.” Rule OldCustomer is interesting for two other reasons… first, notice that we have two conditions we check for: That the date of the last order was more than 90 days ago, and that the Status is not already equal to Re-Activate. (Without this second condition, you would find yourself in an infinite loop, for the rule would always be true… more on this later).

Note that in a rule, an AND condition is implied. In other words, two conditions in a rule are assumed to be ANDed. If you want to make them OR’d, you have to specify it explicitly. (Although a rule purist would tell you that there is no such thing as an OR condition in the Cognitive model… I won’t debate that here…)

The second thing that is interesting, is that we have an action which calls an internal method from Drools called modify().

Modify does not do what you might think – it does not actually modify the object. That is done by the previous statement: cust.Status = "Re-Active". The Purpose of modify() is to tell the rules engine that we did something to the object – which means that it has to be re-evaluated to see what new rules may apply to it.

Do you see now why the second condition is needed? Otherwise, this rule would fire forever. We are changing the Status property, and asking the rules engine to check to see what rules apply for it. The DaysSinceLastOrder is still over 90, so the rule fires again, and again changes the status, and again tells the rules engine to check it… on and on forever.

The third rule seems boring again, but it actually is interesting when we run the scenarios. The reason is that none of our objects ever have the Status initially set to Re-Activate. The only way the Status can be set to Re-Activate is by the OldCustomer rule. When this happens, it will trigger the need for the second rule to also examine the object, and (if appropriate) send us the discount information.

This is referred to as Forward Chaining. One rule takes an action which requires a second rule to be evaluated. It is the bread and butter of a rules system. Without forward chaining capabilities in a rules engine, all you have is a very fancy switch/case set-up.

OK, back to the project…

According to the documentation in the Drools.Net project web page, you have two options for starting a new rules service, but I can only get one to work.

I put my code in the FORM_SHOWN event code, since I want to have feedback given to the user while the rule base is loaded. This code (minus debug printouts) looks something like this:

 _thisForm = this;
PackageBuilder builder = new PackageBuilder(); 
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MinimalDroolsForm.SimpleRules.drl");
builder.AddPackageFromDrl("MinimalDroolsForm.SimpleRules.drl", stream);
Package pkg = builder.GetPackage();
ruleBase = RuleBaseFactory.NewRuleBase();
ruleBase.AddPackage(pkg);
workingMemory = ruleBase.NewWorkingMemory();
workingObj = new CustomerRecord();

The first line sets a static variable that references the form itself. I use this so the rules can access the textBox on the form.

Then comes the fun stuff. The Assembly.GetExecutingAssembly… is code that opens a stream to the DRL file which is embedded in my projects resource file.

Then I go through the steps to load the rules file, using that stream, and putting it in the new package, and finally adding that to the rulebase. (BTW the AddPackageFromDRL function takes several seconds to run… be patient…)

The last step for loading the rules base is to declare a new area of working memory. This is the area that the rules look at when they calculate which apply, and which don’t. If it ain’t in the working memory, as far as the rules are concerned, it doesn’t exist.

Now, we’re ready to go, but nothing will happen until we click the “Run Rules Button”.

That is when the object’s values are set, and then it is calls the function that “runs” the rules:

TestRules(ruleBase, _thisForm);

If you look inside this function, you will see what actually does the work:

if (_currentCust == null)
  {
     _currentCust = workingMemory.assertObject(workingObj);
  }
else
 {
    workingMemory.modifyObject(_currentCust, workingObj);
  }
workingMemory.fireAllRules();

The reason for the IF statement is that there is no sense in creating an object more than once. If we already have it, we simply modify it.

Finally, we call the unfortunately named: FireAllRules() method. (In reality, not all rules fire – just those that apply.)

After the rules are done processing, I print the values of the customer object in the form’s text box, so we can verify that the rules are doing their job.

To make things easier, I have three scenarios loaded, and you can see what they do by clicking on them, and hitting the run rules button.

Scenario 1 is really boring. It fires no rules.

Result:

Rule Run:

Customer: 111-111 | Qty: 5 | STATUS: NORMAL

Scenario 2 is interesting, because it causes the forward chaining that we discussed earlier:

Result:

Rule Run:

RULE FIRED: Old Customer

RULE FIRED: Offer WELCOME BACK discount

RULE FIRED: Large Order

Customer: 222-222 | Qty: 500 | STATUS: Re-Active

Scenario 3’s result may not be intuitive if you don’t understand how pattern matching works. For Scenario 3, I create TWO objects in the working memory.

Result:

NOTE: THIS SCENARIO INCLUDES AN ADDIONAL OBJECT

Rule Run:

RULE FIRED: OldCustomer

RULE FIRED: OldCustomer

Customer: 333-333 | Qty: 50 | STATUS: Re-Active

2nd Customer: 111-111 | Qty: 5 | STATUS: Re-Active

Notice How the Old Customer Rule will process each object in the pattern. So, one rule handles both objects, because it matches each one. The use of a pattern allows us to deal with each instance of the object individually.

It is this granularity which permits us to write a rule that is very focused on what it must do. Because it deals with any and every object that matches its conditions individually, it can operate with confidence that its actions are appropriate.

Put another way, a rule operates as a “Whenever… then…” not as “If… then…”

If I were writing this procedurally, I would have to write some sort of a while loop to run through all instances of the objects. I would need to check all of the code before and after the if/then, because in procedural code, everything matters. Making a change is can get tedious, because I always need to concern myself with the surrounding control code.

(There is a name for not checking the rest of the code when making a change – it’s called hacking.)

But in Rules “Programming” it is not hacking. The patterns on the condition side of the rule have filtered out everything else in the universe, so all we need to know about is what is right there in front of us. We can feel free to change the status of our customer, because if this customer didn’t need his status changed, he would not be in front of us on the RHS of the rule.

That probably needs its own article…

And... I have rambled on long enough, I hope you found this exercise helpful.

A couple of quick notes:

Drools.Net is behind the development cycle of its Java based cousin. As I write this, Drools.NET is at the 3.0 release, but the Java Rules version (JBOSS Rules) is already at 4.0. There have been some syntax changes in 4.0, so if you go online to get the documentation, make sure you get the right one!

Unfortunately, the documentation for Drools.NET is nowhere near as good as the Java Version. For things such as syntax, that doesn’t matter, since the syntaxes are (mostly) interchangeable. You can learn from the Java version, and then implement in Drools.NET.

One real drawback to developing in Drools.Net is that the debugging features are not nearly as good. The Java version has a plug-in for Eclipse that makes it far easier to code the rules there.

My biggest gripe with the Drools.NET version is that syntax errors in a rules file can give unpredictable results. For example, when I first started building my test application, I was using the 4.0 docs and mistakenly called a function from one of my rules which doesn’t exist in 3.0. Instead of throwing a compile error, Drools.NET just failed when it tried to load the file – and it still didn’t give me an error.

You see, when you load a rules file, the Drools.NET compiler will compile the result in a file called CompiledRules.dll. It then uses this for its executions. When my rules file could not load, the program didn’t create the new CompiledRules.dll. No error was shown. But when the program went to run the rules, it used the LAST compiled.dll it had – so basically, it was using old rules, and I didn’t know it. This took awhile to figure out.

All in all, I would say that JBOSS Rules is an incredibly useful open source project. Drools.NET could use some more help to get it ready from PrimeTime. Maybe someone reading this will be inspired and get involved!

History

Article Submitted: 9-8-2008

UPDATED: 9-23-2008

The previous archive file did not include some needed resources. You could build them yourself, but it wasn't obvious so I have included a new ZIP file. Note: I use WinRar, not PKZIP, so you may need to downlad and use WINRAR to see the files (codeproject will not allow me to include an archve that has an RAR extension, so I added the .ZIP... but it is a RAR file. With WinRar, btw, you can unpack both zip files and RAR files...)

There is a CHANGE in this version, and that is that the rules file (.DRL) is now loaded externally. It is NO LONGER embedded in the project. Thanks again to Scott McMasters who read this article and emailed me the correct code for that. Because I do not know where you will install this, I expect you to make a change to the directory before you compile it. You can search in the Form1.CS file, at line 42. Change the String that says:

YOUR PROJECT DIRECTORY HERE to whatever your project directory is.

Enjoy!

License

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


Written By
Chief Technology Officer SAINT Police Systems
United States United States
Mark Grice is co-founder and CTO of SAINT Police Systems. We are focused on creating next-Gen software for Police Departments, specifically to increase Situational Awareness in the field.

Prior to his current position, he was involved in several companies working with Expert Systems. He began his career with Inference Corporation where he worked with both Rules systems and Case Based Reasoning solutions.

He was hired away from Inference by a competitor, Neuron Data. where he eventually became the product manager of what was to become "Blaze Advisor."

Comments and Discussions

 
QuestionDrools.NET seems to be now unsupported Pin
MarkJoel6030-Jan-18 4:27
MarkJoel6030-Jan-18 4:27 
QuestionThe all 5 dll's are missing from all the zip folder, Where do I get these dll's? please resolve this asap... Pin
mecramesh14-Mar-16 0:05
mecramesh14-Mar-16 0:05 
AnswerRe: The all 5 dll's are missing from all the zip folder, Where do I get these dll's? please resolve this asap... Pin
sdphg17-Mar-16 22:35
sdphg17-Mar-16 22:35 
GeneralRe: The all 5 dll's are missing from all the zip folder, Where do I get these dll's? please resolve this asap... Pin
Member 125952218-Feb-17 3:14
Member 125952218-Feb-17 3:14 
QuestionWhere do we get the 5 dlls? Not present in the zip and there are no references to download it either. Pin
Member 1142585313-Mar-16 22:55
Member 1142585313-Mar-16 22:55 
AnswerRe: Where do we get the 5 dlls? Not present in the zip and there are no references to download it either. Pin
sdphg17-Mar-16 22:36
sdphg17-Mar-16 22:36 
QuestionReferenced component drools-3.0 could not be found Pin
Member 114258534-Feb-15 1:52
Member 114258534-Feb-15 1:52 
QuestionDrools.Net and DateTime Objects Pin
jolen12323-Jul-14 7:00
jolen12323-Jul-14 7:00 
QuestionGetting NullReferenceException while using Drools 3.0 Pin
Shyam Dixit3-Mar-14 4:34
Shyam Dixit3-Mar-14 4:34 
QuestionHelp - Drools .NET 3.0 - List C# Pin
Fábio Moroni20-Sep-13 5:01
Fábio Moroni20-Sep-13 5:01 
Questionquery on drolls.net Pin
Member 259548823-Sep-12 20:33
Member 259548823-Sep-12 20:33 
AnswerRe: query on drolls.net Pin
MarkJoel6030-Nov-12 3:26
MarkJoel6030-Nov-12 3:26 
Questionthe document ! Pin
bos_kg21-Sep-12 17:08
bos_kg21-Sep-12 17:08 
GeneralMathematical Operations in Rules Pin
rahul-techie26-May-11 4:34
rahul-techie26-May-11 4:34 
GeneralRe: Mathematical Operations in Rules Pin
rahul-techie26-May-11 4:59
rahul-techie26-May-11 4:59 
GeneralTrouble getting DRL files working with .NET Pin
Member 776298018-Mar-11 9:48
Member 776298018-Mar-11 9:48 
QuestionRe: Trouble getting DRL files working with .NET Pin
ChristabelleGalea11-Mar-12 4:48
ChristabelleGalea11-Mar-12 4:48 
GeneralRe: Trouble getting DRL files working with .NET Pin
YamilBracho2-Aug-13 10:58
YamilBracho2-Aug-13 10:58 
GeneralImport external package in a rule causes exception Pin
Member 28566558-Mar-11 19:58
Member 28566558-Mar-11 19:58 
Generalweb application don't work [modified] Pin
primoz198724-Dec-10 4:58
primoz198724-Dec-10 4:58 
GeneralRe: web application don't work Pin
Pratik BHAVSAR20-May-11 0:50
Pratik BHAVSAR20-May-11 0:50 
GeneralProblem with Double-Values! Pin
J1mp4n5327-Sep-10 2:43
J1mp4n5327-Sep-10 2:43 
Generaldrl file Pin
wangpeng12313-Apr-10 21:43
wangpeng12313-Apr-10 21:43 
GeneralRe: drl file Pin
MarkJoel6024-May-10 4:32
MarkJoel6024-May-10 4:32 
Generalexception while trying to initialize new package [modified] Pin
deostroll10-Mar-10 23:34
deostroll10-Mar-10 23:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.