Custom collections and databinding, part 2: filtering.

In my last post I presented myimplementation of a class that incapsulates the
logic for supporting the databing of a generic custom collection.

Now I want to discuss the way I implemented the capability to filter data presented the consumers of my class. What I was looking for and I obtained is the possibility to apply a filter in the way of a complex filter expression like ” AnyProperty = ‘AnyValue’ AND AnotherProperty > = ‘AnotherValue’ AND NOT ThirdProperty LIKE ‘%ThirdValue%’ “.

I’m going to present my solution and I hope to read your comments, opinions,
and so on. Remember that my class, CollectionView
, accept as source any class that implements ICollection and that consequently
I must be able to apply that kind of filter expression to any kinf of object.

Consider the example:

ArrayList list = new ArrayList();list.Add(new Blogger(“Albert Einstein”, “blogs.genies.org/albert”, 45));

list.Add(new Blogger(“Jack Keruoac”, “blogs.beatfoundation.org/jk”, 24));

list.Add(new Blogge(“Henry Miller”, “blogs.lesparisiennes.fr/henry”, 36));

CollectionView view = new CollectionView(list);

view.FilterExpression = “Age >= ’36’ AND BlogUrl LIKE ‘blogs.beatfoundation%’ AND NOT Name LIKE ‘Alb%'”;


in which Blogger is a very simple class with only 3 properties:
public int Age,
public string BlogUrl and
public string Name.

I divided the logic needed to apply the filter into 3 classes: CollectionViewFilter,
ConditionEvaluator, ExpressionEvaluator.
All these 3 classes are declared internal in my class library as the only
property CollectionView exposes for applying the filter is a string FilterExpression {get; set;}.
Let’s examine these classes.

CollectionViewFilter.

This class is the entry point for the filtering process. It contains the
routines needed to extract from the filter expression the tokens that represent
the single conditions. The filter expression needs to be passed to constructor
and the tokens extraction is done with the help of a regular expression. So
considering the example above, it split that filter expression in these 3
tokens:

  • Age >= ’36’
  • BlogUrl LIKE ‘blogs.beatfoundation%’
  • Name LIKE ‘Alb%’

The class exposes only one public method that is: public bool MatchObject(object obj). The purpose of this method is obviouisly returning a bool value that indicates if the object in argument meet all the conditions (expressed by the 3 tokens) or not. I do this by extracting from every token the property name (i.e.: BlogUrl), the operator (LIKE) and the property value (‘blogs.beatfoundation%’). I delegate the process of verifying if the property BlogUrl of any instance of the Blogger class is LIKE ‘blogs.beatfoundation%’ to the ConditionEvaluator class.

ConditionEvaluator.

This logical task accomplished by this class is quite simple, I will not discuss over it’s implementation. It accept in the constructor the property name, the operator and the property value that CollectionViewFilter class extracts from each token, then it performs these steps:

  • Discover via reflection the property descriptor for the specified property
    name.
  • Receive the instance of the object that needs to be matched with the condition
    (the same instance received by CollectionViewFilter
    in MatchObject()
    method, in the case of my example an instance of the Blogger class.)
  • Perform the necessary comparison based on: the property type, the specified
    operator and the jollychars if any in the value.
  • Returns a bool value indicating if the passed instance meets the condition
    (BlogUrl LIKE ‘blogs.beatfoundation%’).

CollectionViewFilter repeats the operation for each token it extracted from the filter expression. So it use ConditionEvaluator for evaluate on the instance passed to MatchObject() method all the conditions expressed by the 3 tokens. This will cause the original filter expression

Age >= ’36’ AND BlogUrl LIKE ‘blogs.beatfoundation%’ AND
NOT Name LIKE ‘Alb%’
“;

to be translated is a string like:

true AND true AND NOT true” (the case of
new Blogger(“Albert Einstein”, “blogs.genies.org/albert”, 45)
).

ExpressionEvaluator .

Now, how can I simply translate a string like the one obove (or a more
complex one) in single evalutaed bool value? Why the magic C# team didn’t
implemented an Eval function like the one JScript has? And the I told to my
self: “Hey! wait a moment. As I can compile some JScript statement I can get
the Eval function in my C# runtime!!”

And I did it this way in my ExpressionEvaluator class,
look at this snippet:
private static object evaluator = null;private static Type evaluatorType = null;

private static readonly string jsSource =

@”package ExpressionEvaluator

{

class Evaluator

{

public function Eval(expr : String) : String

{

return eval(expr);

}

}

}”;

static ExpressionEvaluator()

{

ICodeCompiler compiler = new JScriptCodeProvider().CreateCompiler();

CompilerParameters parameters = new CompilerParameters();

parameters.GenerateInMemory = true;

CompilerResults results = compiler.CompileAssemblyFromSource(parameters, jsSource);

Assembly assembly = results.CompiledAssembly;

evaluatorType = assembly.GetType(“ExpressionEvaluator.Evaluator”);

evaluator = Activator.CreateInstance(evaluatorType);

}
The JScript statement contained in jsSource is compiled only once in the
static constructor (need a reference to microsoft.jscript.dll) and is available
for functions like:
public static bool EvalToBoolean(string statement){

object o = EvalToObject(statement);

return bool.Parse(o.ToString());

}

public static object EvalToObject(string statement)

{

BindingFlags flags = BindingFlags.InvokeMethod;

object[] args = new object[] { statement };

return evaluatorType.InvokeMember(“Eval”, flags, null, evaluator, args);

}
This made my class CollectionViewFilter able to
simply resolve “true AND true AND NOT true” into “false”.The entire process is launched by CollectionView whenever
it has to build is inner source from the ICollection
passed in his constructor or when is property FilterExpression
changes to determine which of the elements in the source collection should be
passed to the dataconsumer.

UPDATE: I forgot a little thing: in order to make a string like
“true AND true AND NOT true” evaluable by the JScript routine you have to
replace the logical operators, in particular you must replace “AND” with
“&&”, “OR” with “||”, “NOT” with “!”.

Advertisements

~ by Matteo on November 8, 2005.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: