Using the WF rules to iterate over a collection

I thought I understood how the WF Rules iterated over a collection.  Isn't that always how it goes - you really prove it to yourself when you can explain it to someone else.  Well, I started to explain how I thought it worked and this person started asking questions and I soon realized that I was no longer sure I understood it.  After a bit of research and with my newly obtained understanding I tried to explain it again and it actually made sense.

So, I have decided to blog about this since I am sure that others will most likely need to do this and may even need to explain it to someone else.

During my research I found the MSDN article titled Introduction to the Windows Workflow Foundation Rules Engine which had a little blurb towards the bottom titled Collection Processing which I found to be a good start.  I also found Matt Winkle's blog entry which had an example that he put on the Workflow Community Site.  It was this example that really solidified it for me.

he had created 4 rules

Rule 1: Initialize with a priority of 2

IF
    1==1
THEN
    this.enumerator = this.OrderItems.GetEnumerator()

Rule 2: IterateOverItems with a priority of 1

IF
    this.enumerator.MoveNext()
THEN
    this.currentItem = (RulesWithCollectionSample.OrderItem)this.enumerator.Current
    System.Console.WriteLine("Assignednumerator" + this.currentItem.Price)
ELSE
    System.Cosole.WriteLine("we are all done")

Rule 3:   IndividualItem with a priority of 0

IF
    this.CurrentItem != null
THEN
    this.Total = this.Total + this.CurrentItem.Price * this.CurrentItem.Quantity
    System.Console.WriteLine("Running Total: " + this.Total.ToString())
ELSE
    System.Console.WriteLine("current item is null")

Rule 4: Finished with a priority of -1

IF
    this.CurrentItem == this.CurrentItem
THEN
    System.Console.WriteLine("Finished True")
    Update("this/enumerator")
ELSE
    System.Console.WriteLine("Finished False")
    Update("this/enumerator")

In addition to these rules you also have to decorate your class with three additional properties. 

First, create an enumerator property that is of type System.Collections.Generic.IEnumerator<T> or System.Collections.IEnumerator

Second, create an items collection to contain your collection of items which can be of type List<T> or anything that supports IEnumerator

Third, create a property to hold the current item

After looking at the rules and the properties I was ready to watch it run. 

Rule 1 ran first, as it should since it has the highest priority and has a condition that always returns true.  This rule populates the enumerator property backing store.  Then the rules engine looks for the rule with next highest priority, which is Rule 2.  This rule calls MoveNext on the enumerator and if it is able to move then true is returned and the THEN part of the rule runs which passes the current collection item to this.currentItem.  If MoveNext return false then the text "we are all done" will be written to the Output Window which is the last item output after all of the items in the collection has been iterated over.

If the MoveNext method in Rule 2 returned true, then the rules engine again looks for the next rule with the highest priority, which is now Rule 3.  Rule 3 checks to see if there is a Current Item and if so does some work (in this case updates the total).  If the Current Item is null then we will get the text "current item is null" written to the Output Window.  However, this line is never encountered when the rules run.

Finally the fourth rule is run, the condition can really be anything but it needs to be evaluated each time, which updates the engine telling it that we want to update the enumerator and force forward chaining behavior.  By doing this we are saying that the engine should look for any rules that have conditions that are reliant on the enumerator.  In this case, Rule 2 matches this and the engine loops to Rule 2 and continues to run through the rules based on priority from there.  This will then set off a loop in the rules engine moving from Rule 4 back to Rule 2, then Rule 3 and Rule 4 until the MoveNext function in Rule 2 returns false (indicating it has no more items in the collection) and the ELSE portion of the rule runs.  At this point the rules are finished processing and the control is returned back to the calling application.

Hopefully this helps explain what is required to iterate over a collection as well as what is happening during the iteration process.