Navigation

Categories
Show Navigation Next Topic  »

Collection support in Code Effects

Code Effects business rules engine natively supports properties of types that implement the IEnumerable and IQueryable interfaces, except those that implement 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; }
}
}

Rule Editor 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. Rule Editor 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 Rule Editor implements the value typed collections exactly like it implements fields. Let's run Rule Editor with our TestSource class as its source object to see that in action (with all reference typed collections commented out for now):

    collection-data-type

    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:

    collections-data-type

    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:

    collections-data-types

    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, Rule Editor 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. Rule Editor 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:

    reference-collection-data-type

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

    reference-collection-types

    Select the TestClasses item. Rule Editor 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:

    collection-types-reference

    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:

    collections-types-reference-two

    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, Rule Editor ignores it:

[Field(CollectionItemType = typeof(Int32))]
public ArrayList ListOfUndefinedType { getset; }

Post your questions on Stackoverflow and become a part of our growing community

Comments: 0