I'm going to talk a little bit here about the expression model used by WF rules, and some of the idiosyncrasies, caveats, and downright quirks that arise from our chosen expression model.
First, a little history. Rather than (re)inventing our own private Rule expression object model, the WF Rules team opted to leverage an existing expression model, namely CodeDom. CodeDom was originally intended as an object model for code emitters: i.e., VS-based designers like WinForms, and the like. The object model forms an abstraction that enables these designers to target C# and VB developers without having to bake language-specific notions into their tools.
WF Rules takes CodeDom to the next level by enabling semantic validation and execution of CodeDom trees. Using CodeDom, WF Rules supports a very large expression language subset that is very close to what C# and VB support. If you can model it in CodeDom (and sometimes even if you can't, as we'll see), you can execute it using WF Rules. It's actually pretty heady stuff, and includes complex name resolution, type checking, and method overloading. In future releases we will consider operator overloading as well.
CodeDom is a fairly rich object model for expressions, supporting a large subset of arithmetic and programmatic expressions. However, it has some annoying gaps.
Value Inequality: CodeDom's CodeBinaryOperatorType enumeration supports "ValueEquality", but there's no "ValueInequality" operator. That's like being able to write "x == y", but not "x != y". This does seem quite limiting, especially in a Boolean-heavy environment like Rules. Fortunately, there's a simple way to express inequality in terms of the supported "ValueEquality":
x != y → (x == y) == false
In CodeDom, this would be constructed using something like:
new CodeBinaryOperatorExpression(
new CodeBinaryOperatorExpression(
x,
CodeBinaryOperatorType.ValueEquality,
y),
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression(false))
Left shift, right shift, and bitwise XOR: While CodeDom supports bitwise AND and bitwise OR operations, there is no support for left or right shifting, and no support for the XOR operator. I don't lose much sleep over this, since these operators aren't often used, especially in high-level Rule Sets.
Boolean NOT: The unary "not" operator cannot be modeled directly in CodeDom. (Actually, CodeDom does not support any unary operators.) Once again, this seems kind of limiting for a Boolean-heavy feature such as Rules, but again there's a fairly simple workaround that is very similar to our "Value Inequality" transformation above:
not x → x == false
In CodeDom, this would be constructed using something like:
new CodeBinaryOperatorExpression(
x,
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression(false))
As we can see, the Boolean "not" operation is just a variant of the "Value Inequality" case.
Unary Negation: The unary negation operator cannot be directly modeled in CodeDom. Fortunately, negation can be expressed in terms of binary subtraction:
-x → 0 - x
In CodeDom, this would be constructed using something like:
new CodeBinaryOperatorExpression(
new CodePrimitiveExpression(0),
CodeBinaryOperatorType.Subtract,
x)
Bitwise NOT: This is another unary operator that cannot be modeled directly in CodeDom. In theory, this operation can be implemented using the substitution:
~x → x xor 0xFFFFFFFF
(assuming "x" is an unsigned 32-bit integer). However, recall that CodeDom cannot model the XOR operator either. So we're stuck in this case; CodeDom cannot represent bitwise NOT. However, like the shift operators, I believe bitwise NOT is a rarely used operator, which we can live without.
WORKAROUNDS
In the end, if an operator is not explicitly supported by CodeDom, its result can almost always be achieved by calling a helper method.
WF Rules also supports custom extensions to CodeDom, so you can add whatever missing operations you like and use them in your rule expressions. I'll try to cover this in more detail in a later article.
DESIGN-TIME IMPLICATIONS
The Condition editor and the RuleSet editors have a text-based, parser-driven interface that allows WF Rules users to enter their expressions in a natural syntax, and not be too particularly concerned with the fact that it's doing nothing more than building CodeDom. In fact, the parser behind it will happily consume the following expressions:
-x
!x
x != y
and do the appropriate transformations into CodeDom for you automatically.
In a future article, I'll talk about more design-time implications that arise from our use of an expression object model to represent Rules expressions.