Aug 27, 2007

How To Use the Windows Workflow Rules Engine By Itself

Windows Workflow Foundation (WF) is a great technology for adding workflow functionality to any .NET application and it includes within it a very useful forward chaining rules engine. Because the rules engine is part of WF there is an unfortunate presumption that in order to use the rules you need to be running a workflow; but who wants to do that just to run a few rules across their objects? Especially when instantiating a workflow requires quite a bit of work or using a workflow just doesn't make sense.

Well, as it turns out you can use the workflow rules engine on it's own without any workflows at all. Because the windows workflow foundation architecture is very loosely coupled the workflow rules engine is effectively a standalone component that workflows call out to when they have rules to evaluate. We can take advantage of this from our own .NET applications and call the rules engine whenever we want a set of rules evaluated against one of our objects.

Why would we even want to do this?

  1. Flexibility.
    Why hard code business rules into your application? Every time a rules changes you need to recompile and redeploy your code. Wouldn't it be great to have all these rules available as configuration data so that all that is required to change something is to edit the rule definitions - no recompilation and no redeployment.
  2. Ownership.
    When rules have to be hard coded into the application they become the property of the developers. If the rules exist as configuration data and can be modified by business people then the rules can be owned and managed by those same people, not the developers.
  3. Transparency.
    What happens when a rule isn't defined correctly or information isn't processed as expected? Normally a developer has to jump into the debugger, step through the code, find the right part of the code and check what's going on. Externalizing the rules makes it easy for non-developers to examine the rules are and see where the problem is occurring.
  4. Power.
    It's easy enough to represent simple rules in code using if-then-else syntax, but if you've ever tried working through rule priorities or forward-chaining of rules (where one rule down the chain means a previously evaluated rule needs to be re-processed) then you know it's not an easy thing to do. The maintenance headache it represents can be daunting. By using a rules engine it becomes much easier to represent collections of rules that would be very difficult to implement in code.

O.K. so if you're still with me then you're probably wanting to know how it all works.

Step 1: Create A Way to Enter Rules

To start with, we need some way to enter rules and somewhere to store them. And, like all good developers, we don't want to reinvent the wheel. Go to the netfx3 web site; specifically the rules engine code samples page and once there download and install the ExternalRuleSetDemo sample.

The demo shows you how to create and edit sets of rules and store them in a database. Normally the rules editor is only available from within Visual Studio as part of the workflow editor but the ExternalRuleSetDemo shows how you can host the RuleSet Editor in a standard windows application and provide a backing store for the rules using an SqlExpress database.

The solution in the sample kit contains 4 projects but you really only need to look at the ExternalRuleSetLibrary project and the RuleSetTool project.

By the way, you don't have to use SqlExpress as the backing store. It's a fairly trivial process to switch it to another provider. Just go to the SaveToDB() and GetRuleSets() methods in RuleSetEditor.cs and change the code. For fun, I switched to an SqlCe Database with very little effort.

Once you've compiled the application, run it and you should see something like this (but without any Rule Sets)

wfrules1

To create a rule set, click the New button. A new ruleset will be created with some default information. You can then indicate what class to base your rules on by clicking the browse button, locating the assembly that contains the class and clicking OK.

Next you can click the Edit Rules button to start the standard WF rules editor window as shown here:

wfrules2

Now you just need to create the rules you need and you should be ready to move to the next step (don't forget to save your rules!). There is one thing to note; you can create rules using any of the properties and methods of the target class - i.e. public, internal and private properties and methods. If you will be calling the rules engine from an assembly different to the business class then you will need to restrict the yourself to the public properties and methods or you will get runtime access permission errors.

Step 2: Calling the Rules Engine

Ok, so now we have a way to create and edit rules. That's great, but pretty much useless unless we have an application in which to use those rules.

Thankfully, the task of talking to the rules engine is actually fairly simple. What we need to do is create a helper class that will:

  • Retrieve (and deserialize) the rules from the database.
  • Validate the rules against the class we want to run them against
  • Execute them against the object we choose.

And then we'll use that helper class to run the rules for our business object.

If you go to the samples site again you can download the RulesDrivenUI sample and have a look at the code in there. It's the basis for what's shown here.

Now, lets load up the rule set. We'll pass in a string containing the name of the rule set we want to load and then load it up from the database.

        public void LoadRuleSet(string ruleSetName)
{
WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
if (string.IsNullOrEmpty(ruleSetName))
throw new Exception("Ruleset name cannot be null or empty.");
if (!string.Equals(RuleSetName, ruleSetName))
{
_ruleSetName = ruleSetName;

ruleSet = GetRuleSetFromDB(serializer);
if (ruleSet == null)
{
throw new Exception("RuleSet could not be loaded. Make sure the connection string and ruleset name are correct.");
}
}
}

The WorkflowMarkupSerializer is used to deserialise the XML based workflow that we stored in the database (in step 1). P.S. These methods exist in a helper class, and the ruleSet variable is a private field of the RuleSet type.


GetRuleSetFromDB is shown below and once again it's pretty simple. The code show here uses the SqlCe database provider, just to prove it can be done. You can, of course, use any database mechanism you like.

        private RuleSet GetRuleSetFromDB(WorkflowMarkupSerializer serializer)
{
if (string.IsNullOrEmpty(_connectionString))
return null;
SqlCeDataReader reader;
SqlCeConnection sqlConn = null;
try
{
sqlConn = new SqlCeConnection(_connectionString);
sqlConn.Open();
SqlCeParameter p1 = new SqlCeParameter("@p1",_ruleSetName);
string commandString = "SELECT * FROM RuleSet WHERE Name= @p1 ORDER BY ModifiedDate DESC";
SqlCeCommand command = new SqlCeCommand(commandString, sqlConn);
command.Parameters.Add(p1);
reader = command.ExecuteReader();
}
catch (Exception e)
{
...
}
RuleSet resultRuleSet = null;

try
{
reader.Read();
resultRuleSet = DeserializeRuleSet(reader.GetString(3), serializer);
}
catch
{
...
}
sqlConn.Close();
sqlConn.Dispose();
return resultRuleSet;
}

Note the call to DeserializeRuleSet() [in bold]. This is where the ruleset we retrieved from the database is converted from XML back to a ruleset object. It basically wraps a call to the WorkflowMarkupSerializer as shown:

                StringReader stringReader = new StringReader(ruleSetXmlDefinition);
XmlTextReader reader = new XmlTextReader(stringReader);
return serializer.Deserialize(reader) as RuleSet;

Cool! So now we've got the rules loaded up from the database.


At this point we'd love to be able to just point the rules at an object of our choice and run them, but before we do, we have to check that the rules are still valid. Why? Because when we deserialised the rules from the database we had no guarantee that those rules were still valid for the object we are going to run them against. Method signatures may have changed, properties may have been removed or we may have loaded up rules created for a completely different class. By validating the rules we protect ourselves from (most) run time errors.


To do it we use the RuleValidation class. We also use the RuleExecution class to get an execution context:

        private RuleExecution ValidateRuleSet(object targetObject)
{
RuleValidation ruleValidation;

ruleValidation = new RuleValidation(_targetType, null);
if (!ruleSet.Validate(ruleValidation))
{
string errors = "";
foreach (ValidationError validationError in ruleValidation.Errors)
errors = errors + validationError.ErrorText + "\n";
Debug.WriteLine("Validation Errors \n" + errors);
return null;
}
else
{
return new RuleExecution(ruleValidation, targetObject);
}
}

There are 2 important things happening here. Firstly we are validating the rules by passing in the type of the object we are going to run them against and checking if there are any errors. Then secondly we are creating an instance of the RuleExecution class. This is needed to store the state information used by the rule engine when validating rules. Note that while we are passing in the validated rules and the object we want to execute the rules on at this point we have not yet executed the rules, we have just created the context in which they will run.


So now we come to it. Finally! It's a really simple call to the RuleSet Execute method:

        private void ExecuteRule(RuleExecution ruleExecution)
{
if (null != ruleExecution)
{
ruleSet.Execute(ruleExecution);
}
else
{
throw new Exception("RuleExecution is null.");
}
}

We can now wrap all these methods up into a simple method as follows:

        public void ExecuteRuleSet(object targetObject)
{
if (ruleSet != null)
{
RuleExecution ruleExecution;
ruleExecution = ValidateRuleSet(targetObject);
ExecuteRule(ruleExecution);
}
}


Now to run rules for our class all we need to do is make two simple calls. Assuming we have put the above code in a helper class then we can do something like this:

            WorkflowRulesHelper wfhelper = new WorkflowRulesHelper();
wfhelper.LoadRuleSet("LSCRules");
wfhelper.ExecuteRuleSet(this);

Now all we need to run rules in our classes is these few lines of code.


Pretty cool, hey? If you've got any comments I'd love to hear them :-)

35 comments:

  1. Daniel BartholomewAug 28, 2007, 8:14:00 AM

    If I had a minute to stratch then I would have a look at this - it does seem very cool.

    Nice work on adding the Workflow Engine to existing appllications

    ReplyDelete
  2. I'm curious to know what you would use the rules engine for in practical application. The first use I think of is validating business objects...but I have a problem with that. If one of my rules fails then I need to report back to the user why it failed and possible how to fix it. From your example it looks as if you get a simple T/F result saying "hey you're valid", or "you're invalid!". Curious to know how you would handle that.

    Also, what about rules that are more complicated than just looking at one objects properties? What if you need to look at a collection hanging off the object?

    Thanks.

    ReplyDelete
  3. The rules can be much more complex than a simple true false, and collections hanging off an object can be used (and iterated) quite easily.

    While this post is intended to show you how to use the rules engine on it's own I'm also aware of the need for a worked example as well. I'm planning on doing this, including how to iterate collections in the near future (stay tuned!)

    ReplyDelete
  4. Hi Richard, nice article. I am experimenting with the rules engine for a project at work, so this gave me a lot of good insight for what the engine is capable of.

    I have a question about the implementation of the engine. It looks like a common pattern of using the engine is to pass a single plain old c# object into the engine with all of your "facts" exposed to the engine as properties. In the case of a car insurance app this would be "driver age", "driver state", "adding driver to existing policy", etc. So, the question is, would it be better to pass these facts in already prepared, or can we allow our c# object to perform "getter" logic for these properties at the time that the engine is run. For instance, the property for "adding driver to existing policy" might call an external web service to derive the result. Should this property be filled before the rules engine is called or can we put this logic in the c# class?

    Also, I can find plenty of simple examples online, but do you have any links to good resources where they are demonstrating some more complex activities?

    Thanks!

    ReplyDelete
  5. Hi Steve,

    Thanks for the kind words. Most of the samples seem to be fairly simplistic in general (probably because they are just samples). I'm trying to put together something a little more complex to give a "real world" view of things but it's taking time (getting a decent self-contained scenario together is hard).

    In terms of rule evaluations, all of an objects methods and properties are evaluated at run time, so of there is any getter logic in the object it will be executed at run time (so try to make sure it's quick). I haven't tried it myself but if you have method or property that calls out to a web service it should work OK.

    ReplyDelete
  6. I have a use case for you. That uses a list. Although its current use is in an academic context. I am making an open source event abstraction Framework for .net

    I am a Masterate Student and I am using workflows foward chaining ability to pull event types from a list and establish if event types can be changed to a complex event or are left as they are. They are then executed after their final type is established.

    An example of a complex event in a real life context could be something like double click or a gesture (like in opera).

    If you want to use that use case that is good by me. One question I have is:

    Can i just remove elements from a list that is referenced by a rule set from c# code while the rules are running? or do i need to do something like pause the rule engine for while i am changing variables.

    ReplyDelete
  7. You should be able to remove elements from the list without a problem.

    The deletion itself needs to happen via a method call, and what is likely to happen (depending how you set your rules up) is that the deletion may cause reevaluation of other rules higher in the rule chain.

    ReplyDelete
  8. Nice article Richard. Here's what I envisage using the rules engine for. Please comment.
    A client would like to create an application that configures 100s of parameters (software) that must be set when building the product. This is customization at the time of hardware build. I would like to use the rules engine to accept all these parameters and based on selections, automatically configure the hardware with appropriate firmware etc. This means I would need a rich UI to allow the end user to mix and match options for his custom build.

    Is this a proper scenario for the use of the rules engine? Where does one stop with it. This is taking me back to the days when we programmed in a 4GL HDBMS, expert engine kind of scenario.

    Thanks,
    Ram

    ReplyDelete
  9. @Ram

    Thanks for the kind words :-)

    Yes, the sort of scenario you are thinking of is workable with the rules engine, however I would be inclined to say that you are probably looking at a workflow and rules solution.

    The configuring of hardware and more importantly the order it happens in is quite important and is not something that rules engine do on it's own. This is what workflow is all about :-)

    ReplyDelete
  10. Hi Richard,I was thinking of how to use Rules Engine as a standalone component without WWF.
    This was helpful article for me.

    I've two queries on Rules Engine.

    1. Can the rule condtion and Action part contain any .NET code? In this case wouldn't be a security problem, as the Editor and the Rules framework alows to update the rules dynamically?

    2. How does the rules and actions are actually exectued? Is it intrepreted or is it doing dynamic compilation of the rules(rule's condition and action statements)?

    Thanks!

    ReplyDelete
  11. @Anonymous:

    A1: The rule and action conditions are restricted to the base .NET calls and the methods and properties on the class to which the rules are applied. So _if_ you want to execute arbitrary code you'd have to write and call a method on the class that allowed you to do it.

    I'd have to check what security context the rules engine runs under, but I'm pretty sure it's the same context as the thread that calls it. I could be wrong on that one though.

    A2. Rules and actions are dynamically compiled. This is part of the reason the rules engine needs to revalidate the rules at runtime. It has to reflect over the class the rules are applied to and check that the definition of the class hasn't changed and that the rules aren't being applied to a different class altogether, etc.
    If the WF team had chosen to interpret the rules then the rules engine would be a LOT slower and a helluva lot more complex. :-)

    ReplyDelete
  12. Thanks Richard,
    1. I can do the security on the thread on which the rules are getting executed. This is fine for me.

    2. So, do you mean, it will dynamically generate MSIL from the rules and actions and then do JIT to produce native code and then execute it?

    Thanks!

    But, in the case of Action statements the code would have already compiled and have MSIL for those right? only in the case of Rule's condition and Assignments it has to intrepret isn't? So, why it need to do dynamic compilation?

    ReplyDelete
  13. @Anonymous - If I have the rules serialised to a database (as per the post), they won't be in a compiled state. All that is serialised is the definition of the rule.

    So when I load that definition at runtime I still need it to be compiled before it can be used by my application.

    ReplyDelete
  14. Hi Richard,

    Can you please refer to the the following MSDN thread, which is contracting...I'm confused now:)

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2638432&SiteID=1

    Thanks!

    ReplyDelete
  15. hi richard, pls ignore the earlier post. even the forum says the same.

    Thanks!

    ReplyDelete
  16. hi richard, sorry, the MSDN forum says different only...pls refer to that ...
    Thanks!

    ReplyDelete
  17. @Anonymous. Thanks for that - looks like we both learned something :-)

    I guess I need to dig around in the internals of WF a bit further!

    ReplyDelete
  18. Hi Richard,

    The out-of-the-box rule editor is too much of a C# programming environment for most business analysts (or domain experts)

    To have business users maintain their rules, we have created an easy rule management environment.

    The Rule Manager allows a busines user to define the rules and export them to windows workflow. To get everybody started quickly, we generate a complete .NET solution with the standalone rule engine.

    There is also a free adapter to import existing business objects. No need to re-create an enterpise domain model.

    Let me know what you think.

    The part that currently needs some further attention is the generation of First Order Logic. (IfExists, ForAll). The support to reason over collections within WFrules is a bit cumbersome.

    Marco

    ReplyDelete
  19. This was really nice, to understand the Rules (WWF).

    Thanks Very Much for your effort

    Premson.
    premsonbaby@hotmail.com

    ReplyDelete
  20. A pet peeve of mine, but its also annoying when you are new to this env: it would be nice if you put your "using" statements at the top of your code examples. Heres some helpful ones that will get your code running:

    using System.IO;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.ComponentModel.Serialization;
    using System.Workflow.Runtime;
    using System.Workflow.Activities.Rules;
    using System.Xml;

    ReplyDelete
  21. @AJ - thanks for the feedback. I've normally left out the usings to avoid noise in the code snippets, but I see your point and it's well taken. Thanks again.

    ReplyDelete
  22. This is a wonderful blog. Very helpful and the examples and link were GREAT. Thank you so much!

    ReplyDelete
  23. Great article, but I have a question. In the external rules editor, you have to specify a compiled assembly and it stores the path of that assembly. I have the object to run the rules against. Why does it need to use reflection when the object is loaded through RuleExecution?

    ReplyDelete
  24. Thank you very much my dear dude...:)

    Nice Article.Easy to understand.
    I implemented this in my code

    Working great...

    ReplyDelete
  25. Thank you very much my dear dude...:)

    Nice Article.Easy to understand.
    I implemented this in my code

    Working great...

    Written by Veesam

    ReplyDelete
  26. Is there anyone still responding to this post?

    ReplyDelete
  27. @Chad Other may, but for me; not so much. I've moved on to WF 4.0 which changes the way everything works. That said, rules in WF4 can still be externalised as per this MSDN article - http://msdn.microsoft.com/en-us/library/ee960221.aspx

    ReplyDelete
  28. Has the sample site been taken down? None of the links work for me.

    ReplyDelete
  29. Regarding WF4, I've just written an article on how to approach the same problem using that framework instead of Rules in WF3:

    http://www.nilzorblog.com/2011/11/using-wf4-as-rule-engine.html

    This article was one of several I used in the learning/research process. So thanks, Richard :)

    ReplyDelete
  30. This comment has been removed by the author.

    ReplyDelete
  31. This comment has been removed by the author.

    ReplyDelete
  32. External RuleSet Toolkit Sample:
    http://msdn.microsoft.com/en-us/library/bb472424(v=VS.90).aspx

    After unzipping, find it in \WCF\Extensibility\Rules\ExternalRuleSetToolkit\CS.

    ReplyDelete
  33. Can we use WF rule engine for a Web application or is WF rule engine restricted to window applications only.

    ReplyDelete
  34. @Shuchi First up I wouldn't use the rules engine any more. It was unfortunatley dropped in .NET 4.0, which is a shame.

    That said, if you want to use it in a web application and you're OK with tying your app to an architectural dead end, then yes, you can use it in a Web application. Good luck!

    ReplyDelete
  35. Richard,
    We are also planning to use this rule engine in our application which is 3.5 ASP.net web application.But your last comment"It was unfortunatley dropped in .NET 4.0" has made me lil bit worried...Can you please elaborate what is not supported in 4.0 as I can still see all the rules libraries in 4.0

    Thanks...

    ReplyDelete