Collection support in Code Effects

Code Effects component natively supports properties of types that implement the System.Collections.IEnumerable and System.Linq.IQueryable interfaces, except those that implement System.Collections.IDictionary. This means that collections and arrays declared in your source object can be used in business rules.

Before we get into the details, let's declare a simple source object called TestSource that inherits from a base class TestBase<T>. Both classes declare collection properties:

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using CodeEffects.Rule.Attributes;

namespace CodeEffects.Rule.Harness
{
// The generic base for our source object
public class TestBase<T>
{
// Generic collection; the classes that inherit from
// this base define its type and its UI behavior
public List<T> BaseListItems { getset; }

// Generic array
public T[] BaseArrayItems { getset; }
}

// Our test source object is set to use its base members
[Source(DeclaredMembersOnly = false)]
public class TestSource : TestBase<TestClass>
{
// Value typed generic collection
public List<int> Integers { getset; }

// Reference typed generic collection
public EnumerableQuery<TestClass> EnumerableTestClasses { getset; }

// Non-generic collection
public StringCollection Strings { getset; }

// Collections whose underlying type cannot be determined at design time
// must define it using the new CollectionItemType property of the Field
// attribute. Otherwise they will be completely ignored
[Field(CollectionItemType = typeof(TimeSpan))]
public ArrayList TimeSpans { getset; }

// Value typed arrays
public double[] Doubles { getset; }

// Reference typed arrays
public TestClass[] TestClasses { getset; }

// Declaring a couple of regular fields in order to have
// something other than collections in the editor
public int Id { getset; }
public string Name { getset; }
}

public class TestClass
{
public int Id { getset; }
public string Name { getset; }

// Nested collections are fully supported in 4.0
public List<DateTime> Dates { getset; }
}
}

Code Effects component divides all collections and arrays into two major categories: 1) collections of reference types and 2) collections of value types.

  • Value Typed Collections represent arrays of value types which can be used in business rules as if they were plain value typed properties. Code Effects component allows rule authors to create rule conditions using collections and plain operators such as contains, starts with, and so on. The only operators that are not allowed with value typed collections are is and is not.

    This will look familiar to rule authors who have experience creating rule conditions using regular fields and values, as Code Effects component implements the value typed collections exactly like it implements fields. Let's run the rule editor with our TestSource class as its source object to see that in action (with all reference typed collections commented out for now):

    As you can see, all value typed collections are presented in the menu like regular fields. Their operators look like operators of the regular fields as well:

    IMPORTANT! The collection is empty and collection is not empty operators check if the collection is/is not initialized AND if it contains/doesn't contain items.

    As with any static rule element in Code Effects, developers can use a custom Help XML to change the default English labels of all operators in the rule editor.

    Assigning values to collection conditions is the same as assigning values to rule conditions with the regular fields:

    IMPORTANT! Conditions with numeric type collections can only use ValueInputType.User as value input. Consider using in-rule methods if your project requires a higher level of complexity.

    As you can see, if your source object declares only value typed collections, rule authors will not see many differences in the rule editor from what they are used to when using simple rule fields. Obviously, if you need more than the built-in operators have to offer you can always write your own in-rule methods and implement anything within the bounds of the .NET framework.

    Because your particular project may require special logic when it comes to aggregation, Code Effects component doesn't implement aggregate functions such as Sum, Average, or Count for collections at all. It leaves it up to the developer to implement any kind of aggregation using in-rule methods.

    For example, the following method could be declared in the TestSource class in order to have the Count functionality for generic list properties:

    public int Count<T>(List<T> list)
    {
    	return list.Count;
    }

    Having this method declared in the source object or any other referenced class, we can create the following rule:

    If Count ( Integers ) is equal to [3] then set Name to "John"
  • Reference-Typed Collections represent arrays of reference types. Code Effects component uses its unique rule-in-rule feature that allows the rule authors to query the inner type of the collection right within the main rule.

    To see this feature in action, uncomment all reference-typed collections in our TestSource class, run the project and click inside of the rule area in order to bring up the fields menu:

    Notice the two new fields in the menu, Exists... and Does not exist..., among the expected value-typed collections. Their presence indicates that Code Effects component found reference-typed collections in the source. Select the Exists... item in order to bring up the list of those collections:

    Select the TestClasses item. Code Effects component sets the editor up for the rule author to be able to "dig" inside the underlying type of the TestClasses array which is the TestClass:

    Let's fix a small problem before we go any further. At this point the beginning of the rule reads "If exists TestClasses where...". We can set the value of the FieldAttribute.CollectionItemName property to "Test Class" so the rule could read "If exists Test Class where..." This way our rule would look more like normal English:

    [Field(CollectionItemName = "Test Class")]
    public TestClass[] TestClasses { getset; }

    Alternatively, we could have changed the default label of the Exists... item to Exist... by altering the Help XML if we wanted to keep collection names in our rule plural.

    Now let's get back to our example and create the inner rule that would tell Evaluator which TestClass instances we are targeting:

    As you can see, rule authors can easily work with collections using this rule-in-rule feature. Obviously, inner rules can only be of the evaluation type. The main rule can contain an unlimited number of inner rules which can be nested in an unlimited number of levels. Inner rules can also use the reusable rules.

IMPORTANT! Use the FieldAttribute.CollectionItemType property to set the underlying type of the collection if it can't be determined at design time. If such a collection doesn't have this value set, Code Effects component ignores it:

[Field(CollectionItemType = typeof(Int32))]
public ArrayList ListOfUndefinedType { getset; }
Post your support requests on Stackoverflow.com. You can also post your comments and product feedback using the form at the bottom of this page.
Comments: 0
Name (optional):
Comment (URLs are allowed and must start with http:// or https://; all tags will be encoded):
Remaining character count: