Rule-Based Data Filtering Using LINQ to Objects

This topic describes how to use Code Effects to filter any in-memory collection of objects using evaluation type rules created in the Rule Editor. This feature is based on the standard LINQ to Objects provider built into .NET and requires no special configuration. It works with any IEnumerable<T> value, whether the data originates from a database, an API, a file, or any other custom source.

All examples below assume that:

  • the rule is an evaluation type rule, for example one created in the Rule Editor's Filter mode,
  • the rule was saved as Rule XML, and
  • the variable ruleXml contains that XML.

Overview

The Filter(..) extension method applies a rule to any in-memory sequence: it compiles the Rule XML into a predicate and keeps the items for which the rule is true.

Example:

using CodeEffects.Rule.Engine;

public class Product
{
    public decimal ListPrice { get; set; }
    public string Name { get; set; } = string.Empty;
}

// Rule: "Get records where List Price is greater than [999]"
string ruleXml = GetRuleFromYourStorage();

var products = new List<Product>
{
    new() { ListPrice = 100m, Name = "Clamp" },
    new() { ListPrice = 200m, Name = "Spacer" },
    new() { ListPrice = 1200m, Name = "Crank Arm" },
    new() { ListPrice = 800m, Name = "Front Derailleur" },
    new() { ListPrice = 1500m, Name = "Mountain Tire" }
};

var result = products.Filter(ruleXml);

foreach (var p in result)
{
    Console.WriteLine($"{p.Name}  {p.ListPrice}");
}

This example prints only the products whose ListPrice is greater than 999. The logic comes entirely from the rule stored in ruleXml.

The Overloads

Call Use it when
items.Filter(ruleXml) The ruleset holds one rule, or the first rule is the one to apply.
items.Filter(ruleXml, ruleId) The ruleset holds several rules: ruleId names the one to apply.
items.Filter(ruleXml, ruleId, getRule) The rule calls reusable rules that are not in the ruleset: the getRule delegate returns each one by its ID.
items.Filter(typeof(Product), ruleXml, ..) items is a non-generic IEnumerable: pass the type of its items.

To filter many collections with the same rule, or to set evaluation parameters, create one Evaluator<TSource> and call its Filter(..) method. The evaluator compiles the rule once and can be reused:

var evaluator = new Evaluator<Product>(ruleXml);

var expensive = evaluator.Filter(products);
var expensiveToday = evaluator.Filter(GetTodaysProducts());

When the Rule Runs

The generic Filter(..) compiles the rule when it is called, and evaluates it for each item as the result is enumerated, as LINQ's .Where() does. If the collection changes before you enumerate the result, the result reflects the change. Call .ToList() on the result to evaluate every item at once.

The non-generic Filter(IEnumerable, Type, ..) evaluates every item when it is called and returns a new collection of the items that passed.

Null Handling

By default, the engine adds null checks to the compiled rule. If an object in a property chain is null, the condition that reads through it is false for that item, and no exception is thrown. For example, a rule that checks Customer Address City is Boston skips the customers whose Address is null.

If your data never holds such nulls and you want the leanest compiled code, turn the checks off with the PerformNullChecks property of the evaluation parameters. A null in a property chain then throws a NullReferenceException, as it would in plain C#:

var parameters = new EvaluationParameters
{
    PerformNullChecks = false
};

var filtered = new Evaluator<Product>(ruleXml, parameters).Filter(products);

Methods in Rules

If the source object declares in-rule methods, those methods can be used inside rules. An in-rule method is invoked for each item in the sequence it is evaluated for.

Combining LINQ Operators

You can freely combine standard LINQ operators with Code Effects filtering. Use them before or after calling Filter(..).

Using LINQ before applying the rule:

var active = customers.Where(c => c.IsActive);
var result = active.Filter(ruleXml);

Using LINQ after applying the rule:

var filtered = customers.Filter(ruleXml);
var sorted = filtered.OrderBy(c => c.LastName);

Because all data is in memory, any LINQ operator is supported.

Summary

Code Effects fully supports rule-based filtering of any in-memory collection using the standard LINQ to Objects provider. The Filter(..) method compiles Rule XML into a predicate and applies it to your objects. A filter can use every condition an evaluation type rule can, and it works with null checks, in-rule methods, reusable rules and other LINQ operators as described above.

This feature is ideal for:

  • cached or pre-loaded data,
  • custom object collections,
  • processing pipelines that already work in memory,
  • UI-level filtering,
  • and rule testing without a database.

No configuration is required: if your data implements IEnumerable<T>, Code Effects can filter it.

Ask on Stack Overflow - Yes, it's still active
Ask in GitHub Discussions

p102

l097 --

l102

p101

×