Simple Rules Engine

Here is a rules engine that is internal to the .Net Framework.

This is the kind of rule that I can use:

<RuleSet.Text Name=’Text Ruleset’ Description=’Test Ruleset’ ChainingBehavior=’None’>
<Rule Active=’True’ Name=’R1′ Description=’Checks Name’ Priority=’1′ ReevaluationBehavior=’Never’ >
<Condition>this.Name == null</Condition>
<ThenActions>
<Action>this.ValidationMessages.Add(“Name should not be empty”)</Action>
</ThenActions>
</Rule>
</RuleSet.Text>

The following is a unit test that executes the above:

[Test]
public void FileStringRule()
{
TextRuleSetSerializer rs = new TextRuleSetSerializer();
RuleSet Rules = rs.Deserialize(typeof(Person), File.ReadAllText(@”.Ruleset.Rule”) );
Person p = new Person();
RuleExecution execution = new RuleExecution(new RuleValidation(p.GetType(), null), p);

    Rules.Execute(execution);
Assert.AreEqual(1, p.ValidationMessages.Count);
}

Here is the definition of Person:

public class Person
{
public string Name { get; set;}
public int Age { get; set; }
private List<string> _ValidationMessages = new List<string>();
public List<string> ValidationMessages { get { return _ValidationMessages; } }
}

The beauty of this is that we have externalised the business rules in an efficient, readable compact form that makes no assumptions about the specific types.  The RuleEngine is passed a POCO to act as a Fact.  The Fact object is used to provide the details to the rules and collect the results.

By keeping the business rules externally to the application allows suitable changes to the logic to be made without requiring recompilation.  It is even possible to update the rules used by an application without requiring a redeploy!

However should you be prepared to take a dependency on a specific base class can I suggest that you look at the PerfectStorm.Model. This provides a base class that forms an Aggregate with a fully defined type system. It has the unusual property of being designed to permit invalid input to be entered. This allows an integer field to be set to the string XXX. This allows a single rules interface to perform complete declarative error handling – we don’t use one set of rules for parsing and another for buisiness logic. We can use rules to state what is an isn’t valid by providing an external ruleset. PerefectStorm.Model is intended to be a pure model class. It knows nothing of databases or user interfaces. It can hold data that an external class can use to map to these, but that is not it’s responsibility.

You need to be careful as to the specific goals of the rules engine. They are typically used to answer questions (Is this data valid? Could I save this to my database?) or to provide sensible defaults or to perform calculations (localised tax code calculations). These are also eminently suitable for unit testing and code generation.

Experimenting with powershell and tfs

I have been investigating with controlling powershell with tfs.

Other people have tried such as here and here.

Here is an example of creating a work item in powershell

The Tfs power tools project includes some powershell scripts yet fails to install them by default!

You need to repair the installation and then install the powershell scripts.

Even then you only get the x86 versions so you need to remember to use the x86 ISE/powershell console.

The following is the result of some experimentation with a free visualstudio.com tfs account.

It is not easy to authenticate against this so you need to map to a workspace and use the workspace to identify the server.

#This needs to use the x86 version of the ise
Add-PSSnapin Microsoft.TeamFoundation.PowerShell

[void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.TeamFoundation.Client”)
[void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.TeamFoundation.WorkItemTracking.Client”)

#OK you need to map the server to a local folder that is a workspace

#This returns a real tfs object
$tfs = Get-TfsServer -Path “d:devTFSTesting”

#With some powershell type trickery you can get to the other useful services.

$wit = $tfs.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])

#This shows that you can programatically get a specific work item (I only have this work item).
$wit.GetWorkItem(1)

#This demonstrates changing it
$wi = $wit.GetWorkItem(1)
$wi.Title = “Adjusted”
$wi.Save();

$wit.Projects[“TFSTesting”].StoredQueries | select Name,QueryGuid,QueryText

#Your GUID will vary.
$q = $wit.GetStoredQuery(“a6316d4a-7139-468c-b200-c9754e9de5c4″).QueryText.ToString()

#Example simple query
$wit.Query(”
Select [State], [Title]
From WorkItems
Where [Work Item Type] = ‘Product Backlog Item’
Order By [State] Asc, [Changed Date] Desc”)

#Adding a simple work item

$workItem = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($wit.Projects[“TFSTesting”].WorkItemTypes[“Product Backlog Item”])
$workItem.Title = “Created by Windows PowerShell!”
$workItem.Save()

#More work needs to be done to ensure that the work items have the correct properties .