WF Rules: Unleashing The Rule Engine Within .NET

I have written previously about WF Rules, the rule engine that ships in .NET. As I have described, this rule engine is bundled in .NET versions 3.0 and higher, and is included in the default installation of Windows Vista and Windows 7. (If you are using .NET 4.0, you need the Extended / Full install – the WF Rules assemblies are not in the Client install.)

Getting Access To WF Rules
The average rule author who is looking to try out WF Rules will ask where to start. At they point, they may become lost in the sea of WF documentation or find that they are told to use Visual Studio, which they may not even have installed. It is indeed the case, that WF Rules is typically used as part of Windows Workflow Foundation, and the rules are typically authored within Visual Studio. However, WF Rules can also be used without Visual Studio or Workflow Foundation if one is willing to read through the documentation, download some samples and write a bit code.

A Simple Example
Inspired by a co-worker, I have put together this quick example to lower the barrier to entry a bit. This is a very bare-bones example in order to highlight the rule-specific code that is needed to utilize WF Rules without Visual Studio or Workflow Foundation. So, the code will not feature command-line arguments, a mutable file name, factoring for re-use, etc. (If you are reading this, you most likely already know how to do those things in C#.) The emphasis here is upon demonstrating the scenario with the minimum amount of code. You should be able to paste these three code snippets into three files and have a working example.

The Target Type
In order to author rules, we require a target type to write the rules against. Let’s start with a simple data class:

using System;

class Person
{
  double age;
  public double Age { 
    get
    {
      return age;
    }
    set
    {
      age = value; 
    }
  }
}

For this example, I placed this code in a file named Person.cs. You may choose to place this type within the same file as the other code below. As I said, this example is geared for simplicity.

The RuleSetDialog
Now, let’s write some short code that will instantiate the RuleSetDialog and allow us to author a rule. This code needs to do three things:

  1. Open an existing or create a new .rules file – the WorkflowMarkupSerializer is used if we load an existing RuleSet
  2. Instantiate the RuleSetDialog, while supplying the target type
  3. Save the .rules file if the rules have been updated – using the WorkflowMarkupSerializer

Here is a minimal program to do this:

using System;
using System.IO;
using System.Windows.Forms;
using System.Workflow.Activities.Rules;
using System.Workflow.Activities.Rules.Design;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Xml;

class Program
{
  static string filename = "Test.rules";

  static void Main()
  {
    RuleSet ruleset = null;
	
    // Obtain or create ruleset
    if (File.Exists(filename))
    {
      // load file
      ruleset = Load(filename);
    }
    else
    {
      ruleset = new RuleSet();
    }

    RuleSetDialog dialog = new RuleSetDialog(typeof(Person), null, ruleset);
    DialogResult result = dialog.ShowDialog();

    if (result == DialogResult.OK)
    {
      // save the file
      Save(filename, dialog.RuleSet);
    }
  }

   static RuleSet Load(string filename)
  {
    XmlTextReader reader = new XmlTextReader(filename);
    WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
    object results = serializer.Deserialize(reader);
    RuleSet ruleset = (RuleSet)results;

    if (ruleset == null)
    {
       Console.WriteLine("The rules file " + filename + " does not appear to contain a valid ruleset.");		
    }
   return ruleset;
  }

  static void Save(string filename, RuleSet ruleset)
  {     
    XmlTextWriter writer = new XmlTextWriter(filename, null);
    WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
    serializer.Serialize(writer, ruleset);
    Console.WriteLine("Wrote rules file: " + filename);
  }
}

Authoring A Simple Rule
You will need to save each of these files before you can compile them – for example, I named them Person.cs and RuleEditor.cs.

Now we need to compile these files. You will typically find the C# compiler wherever your .NET installation is, for example:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe

Let me emphasize this – you don’t need Visual Studio for this example. If you have .NET on your machine, you should have the C# compiler already available.

If you saved the files as Person.cs and RuleEditor.cs and pass them to the compiler at the same time, you should end up with RuleEditor.exe – which we can then use to author a rule against a Person. Depending upon your environment settings, you may need to provide the following reference assemblies to the compiler at the same time: System.Workflow.Activities.dll, System.Workflow.ComponentModel.dll, and System.Workflow.Runtime.dll.

Now you should be able to execute RuleEditor.exe and author a rule such as this (click the picture to see full details):
Creating a simple rule in the rule editor dialog

Firing The Rules
Once we have our example rule authored, we need a simple program to load the ruleset and call the rule engine so we can fire the rules. (Note that the code here for loading the ruleset is the same as above.)

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

class Program
{
  static string filename = "Test.rules";

  static void Main()
  {
    RuleSet ruleset = null;

    Person person = new Person();
    person.Age = 70;
		
    if (File.Exists(filename))
    {
      // load file
      ruleset = Load(filename);
    }
	
    if (ruleset != null)
    {
      RuleValidation validation = new RuleValidation(person.GetType(), null);
      RuleExecution engine = new RuleExecution(validation, person);
      ruleset.Execute(engine);
    }
  }

  static RuleSet Load(string filename)
  {
    XmlTextReader reader = new XmlTextReader(filename);
    WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
    object results = serializer.Deserialize(reader);
    RuleSet ruleset = (RuleSet)results;

    if (ruleset == null)
    {
      Console.WriteLine("The rules file " + filename + " does not appear to contain a valid ruleset.");		
    }
    return ruleset;
  }
}

This program should build much the same as the previous program. You will need to include the Person class again. In a real application, we would factor the business objects into a shared library.

Closing
I hope that this bit of code has demonstrated how readily WF Rules can be used by itself. I find it neat to see a rule engine included in a default OS installation.

Resources

11 responses to “WF Rules: Unleashing The Rule Engine Within .NET”

  1. Charles Cherry says:

    Is it possible to use the WF rule engine in a web application? Specifically a .Net MVC app? I know that some components do not fare well in a hosted web environment, so any information you can provide would be helpful.

    Thanks

  2. Karl W. Reinsch says:

    I’m pretty sure that there is a sample showing WF Rules for pageflow on a website. I’ll have to see if I can find a link.

  3. Karl W. Reinsch says:

    Hi again Charles –

    You may find that the pageflow example is helpful. It can be found here: http://archive.msdn.microsoft.com/netfxsamples/Release/ProjectReleases.aspx?ReleaseId=1620.

    There is also a video discussing the sample here: http://channel9.msdn.com/Blogs/mwink/Introduction-to-the-Windows-Workflow-Foundation-Pageflow-sample

  4. Kevin says:

    I have two questions that I cannot find answers to even after searching for a very long time.

    1) If my entity has a field called Type and I want to validate this Type against a collection of say 500 items, either limiting to or excluding from it. I don’t want to hard code all items. How do I do this?

    2) I want to compare two entities so that they cannot have the same combination of Name and Type. The rule does not know about any other entity except the current instance. And the entities do not contain on another.

    Thanks.

  5. AaronLS says:

    Karl, are you familiar with what C# rule engines are currently active/mature? I will probably check out WF but I don’t think it will really fit my needs. Am hoping for something reactive to changes in my in memery entities, similar to how the CLIPS rule engine works.

  6. Michael says:

    This was a great intro. thanks.

  7. Shahab says:

    Simple and helpful, thanks a lot.

  8. Abhijit says:

    Thanks for putting on the simplest and great intro.
    I was trying to validate the rule against collection object but have difficulties doing so.
    i tried searching on google but could not get hold of something of similar nature.
    I have let us say a City class and i have Collection of Cities.

    Class City
    {
    public string name{get;set;}
    public string ID{get;set;}
    }

    Class Person
    {
    public double Age {get;set;}
    public string City {get;set;}
    }

    I have populated City collection from Database.
    I want to run a rule on person object and validate that the City is available in City Collection.

    I know this is old article but hoping the get some response.

    Thanks is advance
    Abhijit
    The person class has a property City.

  9. Karl W. Reinsch says:

    Hello Abhijit. This aspect of WF Rules is often confusing to new users. As I point out in my post comparing Microsoft’s rule engines, WF Rules does not have a notion of Working Memory. So, WF Rules requires all objects to be reachable from a common root object. In other words, you need to attach your City collection to your Person or vice-versa, or attach them to a common root object.

  10. E. Stevens says:

    Excellent article. Got my own project working, a WCF service that fires rule validation on incoming parameters. Thanks for distilling this functionality in an understandable manner. Cheers!

  11. safa says:

    HI,
    I am currently exploring the WF rule engine capabilities, but i have a problem, when i load ruleset from file the rule engine doesn’t execute, it doesn’t throw any exception, but no rule is executed. I am using your load method but it doesn’t work. And I can see thet the ruleset object is loaded correctly so i don’t know what’s the problem. anyone have any idea?

    Thanks,

Leave a Reply