Tag Archives : Rules Engine


A quick and dirty Rules Engine using Windows Workflow (Part 2) 7

Hi there and happy new year.  2011 promises to be quite an interesting year, and I hope that I can continue to contribute here at Sanders Technology.  To kick off the new year, I decided to revisit the Windows Workflow article I started late last year.

A little while ago I wrote a post at entitled “A quick and dirty Rules Engine using Windows Workflow (Part 1)” which has evidently been fairly popular.  Unfortunately, it seems that I forgot to follow it up with a part 2!  Now, welcoming in the new year, I’m putting together the second part.

Honestly though, folks, this could easily be a multi part mini project, because the uses of this Windows Workflow Foundation (WF) rules engine are immense!

I’ve managed to extend the scope of the code displayed in part 1 to include some dummy data items and I’ve crafted some more reusable and general purpose code (for example purposes), but you really ought to be able to see for yourselves how powerful and multi-purpose this really is.

I was going to write a quick and dirty WinForms UI, but I ended up ditching it in favour of a bunch of unit tests instead.  You really should be able to see the potential here, I don’t want to spoil the magic by adding an inept user interface.

Let’s take a look at the sample solution.  I’ve added some terribly (and perhaps insultingly) simple “objects” which, of course, you would substitute for your own DTOs/Entities/BusinessObjects.  It’s a basic class with some public properties, nothing terribly complex (it’s a demo after all).  You can see it uses an Enum just for fun on one of the properties.  I’ve also included a screenshot of the Solution structure – nothing too scary here.

imageimage

The Class View for the “BusinessObjects” | The Solution Structure

Basically the entire solution consists of two class libraries and a Unit Test project.  I’m trying to keep this very simple.  You could plug a WinForms UI or a website or a WCF Web Service Application underneath this very easily!

The main fun is in the “RuleManager” class, which is basically just a wrapper for the main WF workflow engine parts.  I’ve put in an extremely vanilla implementation which allows a few interesting parts of functionality.  I think if you use your imagination, you’ll be able to come up with some much more interesting ways to play with the options.

So why don’t we have a look at the RuleManager class?  It is defined to take a generic type, so you can work upon different source object types.

For the purpose of this post, we have just the one main object defined “Employee”.  It also doesn’t so anything to ensure the rules loaded are explicit for the data type – I’ll do an expanded implementation later to show how we can account for this.

My sincere apologies for the crappiness of the format of the posted code!  I’m having a bit of a fight with my copy of Live Writer and the plugins for inserting code snippets are not working very well with the site layout theme.  I’ll try and get it looking right.. soon.

#region Using Directives
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities.Rules.Design;
using System.Workflow.Activities.Rules;
using System.Windows.Forms;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;
using System.IO;
using BusinessObjects;
using System.Collections.ObjectModel;
#endregion

namespace WorkFlowProvider
{
    /// Implements a wrapper around the Windows Workflow Foundation Rules Engine
    /// A Data Object type to process
    public static class RulesManager<T> where T : new()
    {
        #region Rules Editor Support

        /// Launch the Rules Form to create a new rule
        public static RuleSet LaunchNewRulesDialog(string ruleName, string outputPath)
        {
            return LaunchRulesDialog(null, ruleName, outputPath);
        }

        /// Launch the Rules Editor with an existing rule (for editing),        
        /// or to create a new rule (pass NULL to create a new rule)
        /// The rule name (for the file name)
        /// The path to save rules to
        /// A rule (if one is saved/edited)
        public static RuleSet LaunchRulesDialog(RuleSet ruleSet, string ruleName, string outputPath)
        {
            // You could pass in an existing ruleset object for editing if you 

// wanted to, we're creating a new rule, so it's set to null RuleSetDialog ruleSetDialog = new RuleSetDialog(typeof(T), null, ruleSet); if (ruleSetDialog.ShowDialog() == DialogResult.OK) { // grab the ruleset ruleSet = ruleSetDialog.RuleSet; // We're going to serialize it to disk so it can be reloaded

WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer(); string fileName = String.Format("{0}.rules", ruleName); string fullName = Path.Combine(outputPath, fileName); if (File.Exists(fullName)) { File.Delete(fullName); //delete existing rule } using (XmlWriter rulesWriter = XmlWriter.Create(fullName)) { serializer.Serialize(rulesWriter, ruleSet); rulesWriter.Close(); } } return ruleSet; } #endregion #region Rule Processing /// Applies a set of rules to a specified data object public static T ProcessRules(T objectToProcess, ReadOnlyCollection rules) { RuleValidation validation = new RuleValidation(typeof(T), null); RuleExecution execution = new RuleExecution(validation, objectToProcess); foreach (RuleSet rule in rules) { rule.Execute(execution); } return objectToProcess; } /// Execute a single rule on a single data object public static T ProcessRule(T objectToProcess, RuleSet rule) { RuleValidation validation = new RuleValidation(typeof(T), null); RuleExecution execution = new RuleExecution(validation, objectToProcess); rule.Execute(execution); return objectToProcess; } #endregion #region Rules Management /// Loads a single rule given a path and file name public static RuleSet LoadRule(string rulesLocation, string fileName) { RuleSet ruleSet = null; // Deserialize from a .rules file. using (XmlTextReader rulesReader = new XmlTextReader(Path.Combine(rulesLocation, fileName))) { WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer(); ruleSet = (RuleSet)serializer.Deserialize(rulesReader); } return ruleSet; } /// Loads a set of rules from disk public static ReadOnlyCollection LoadRules(string rulesLocation) { RuleSet ruleSet = null; List rules = new List(); foreach (string fileName in Directory.GetFiles(rulesLocation, "*.rules")) { // Deserialize from a .rules file. using (XmlTextReader rulesReader = new XmlTextReader(fileName)) { WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer(); ruleSet = (RuleSet)serializer.Deserialize(rulesReader); rules.Add(ruleSet); rulesReader.Close(); } } return rules.AsReadOnly(); } #endregion } }

This one class pretty much gives you all you need to create, load and save rules.  It’s a bit basic at this point in time, I will try to create a more robust and tolerant class in subsequent posts on this topic.  For now though, I think it adequately demonstrates the sort of functionality which can be gleaned from the Rules Engine.

You can create or edit a rule by using the LaunchNewRulesDialog or LaunchRulesDialog methods with minimal user input.  I’ve written a very basic Unit Test which proves how efficient this can be, but I’m sure you’ll be able to have some fun with it.

Next up, there are some functions to load existing rule files from disk, the aptly named LoadRule and LoadRules methods.  They are pretty self explanatory, I don’t think we need to go into too much detail about the loading of rules files.

Finally, there are some functions which can be called to execute rules against data objects.  At this stage I’m supporting the execution of a single rule against a single data object, or a collection of rules against a single data object.  Obviously you could easily expand upon this.  You may wish to consider a multi-threaded approach, I may be persuaded to implement a more robust solution which allows for concurrent multiple item/multiple rule processing if you leave a comment for me.

Finally, here’s the Unit Test which allows you to create a new rule and apply it to the test data defined in the test:

[TestMethod]
public void CreateNewRule()
{
    Employee testEmployee = new Employee();    
    testEmployee.FirstName = "Joe";    
    testEmployee.Surname = "Smith";
    testEmployee.Location = StateEnum.ACT;
    testEmployee.Manager = null;
    testEmployee.DateHired = DateTime.Now.AddYears(-1);
    testEmployee.EmployeeNumber = 99;

    string ruleName = String.Format("{0}UnitTestRule", DateTime.Now.Millisecond);
    string path = Assembly.GetExecutingAssembly().Location.Replace(Assembly.GetExecutingAssembly().ManifestModule.Name, String.Empty);

    RuleSet newRule = RulesManager.LaunchNewRulesDialog(ruleName, path);
    testEmployee = RulesManager.ProcessRule(testEmployee, newRule);

    Trace.WriteLine(testEmployee.FirstName);
    Trace.WriteLine(testEmployee.Surname);
    Trace.WriteLine(testEmployee.DateHired);
}

So, in this post we’ve had a look at a very basic solution structure which demonstrates a reusable rules design.  At the moment it is as close to useless as a demo usually starts off looking like.  I’m only getting started, once you are familiar with the ‘RulesManager’ wrapper concept, we’ll be ready to expand upon it significantly.

This is part 2 of a multi-part series.  I’ll be expanding upon the concepts shown here in subsequent posts.

Check back soon!

Solution Files



A quick and dirty Rules Engine using Windows Workflow (Part 1)

Recently I put my mind towards developing a fairly light rules based utility for applying various logical patterns on top of some linear business data.  Honestly, my initial thoughts were “oh no, not another boring rules based implementation” because we (ought to) know how boring that can be – and convoluted – but this time I decided to try something a little different.

Through no fault of my own, I’ve actually had very little to do with Windows Workflow Foundation – WF, not WWF which stands for World Wildlife Foundation, and not to be confused with the World Wrestling Federation, just to avoid confusion!

Most of the project I’ve worked on previously have used some other workflow product, usually something like K2 blackpearl or a custom implementation, for example.  In any case, I’ve always been curious – from what I’d heard it kicks Sharepoint’s “workflow” (if you could call it that) in the butt.  I’m all for that!

Now, from my admittedly sparse knowledge of WF, I knew there was some sort of Rules Engine sitting somewhere near some sort of Workflow for Business Analyst, and it reminded me of a demo I’d seen a little while back involving a graphical interface to design business rules.  Don’t fear, this is way cooler than that!

Anyhow, to cut a long and boring intro short (too late?) I got stuck into designing a quick and dirty rules engine for working with Data Transfer Objects or POCO entities.  Actually, you can pretty much use whatever object you like, it’s very unassuming :)

To start out, you need to add a reference to the following .Net assemblies, available in the .Net Framework v3.0 and onwards:

System.Workflow.Activities
System.Workflow.ComponentModel
system.Workflow.Runtime

So just add them to the project you are working with, and you’re half way there.  Next, lets start off by actually creating some rules, or at least a mechanism to do so.  This is almost too easy for words, so let us take a look at some code instead:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities.Rules.Design;
using System.Workflow.Activities.Rules;
using System.Windows.Forms;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;
using System.IO;

public static class CreateRulesHelper
{
    public static void LaunchRulesDialog(string outputPath)
    {
        RuleSet ruleSet = null;

        // You could pass in an existing ruleset object for editing if you wanted to, we're creating a new rule, so it's set to null
        RuleSetDialog ruleSetDialog = new RuleSetDialog(typeof(BasicTerm), null, ruleSet);
            
        if (ruleSetDialog.ShowDialog() == DialogResult.OK)
        {
            // grab the ruleset
            ruleSet = ruleSetDialog.RuleSet;
            // We're going to serialize it to disk so it can be reloaded later
            WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

            string fileName = String.Format("BusinessRule{0}.rules", DateTime.Now.Millisecond);
            string fullName = Path.Combine(outputPath, fileName);

            if (File.Exists(fullName))
            {
                File.Delete(fullName); //if it bleeds, we can kill it
            }

            using (XmlWriter rulesWriter = XmlWriter.Create(fullName))
            {
                serializer.Serialize(rulesWriter, ruleSet);
                rulesWriter.Close();
            }
        }
    }
}

See? This is a basic implementation inside a static class and essentially launches the WF rules engine editor.  I’m not doing anything fancy here, just taking whatever the user does and serializing it to disk (to be loaded later).  Some key things to point out here:

  1. I’m always creating a new ruleset – you could rewire this to allow editing of existing rules
  2. You may notice I’m using a random file name.  A more detailed implementation could take some input to form the rule name
  3. I’m using a specific object type (“BasicTerm”) – check back for Part 2 when I’ll rework this code so you can use it with different object defintions
  4. This is just a quick mock up!  Sample purposes only :)

Now assuming you had an object called “BasicTerm” and you compiled and ran this code, you’d get prompted with a window not unlike this one:

image image

The best part?  It has IntelliSense too!  You can create some very interesting IF..THEN..ELSE runs here, and you can create multiple rules per rule set.  The neat part is that you can modify the object directly, although it is somewhat limiting, it could be a very handy asset for something like message based routing rules (like in BizTalk).

I won’t go into too much detail at this juncture, other than encouraging you to have a play with this relatively simple interface.  Once you’ve set some rules (or even if you set none) – provided you click OK, you’ll get a .rules file written out to disk.  Check back for Part 2 where I’ll demonstrate how we can use these rules in a quick and dirty workflow.

Continued in Part 2

Further Reading

Introdution to the Windows Workflow Foundation Rules Engine

How to use Windows Workflow Rules