jump to navigation

Persistent AnonymousTypes and Querying with Linq September 19, 2010

Posted by ActiveEngine Sensei in .Net, ActiveEngine, C#, Expression Trees, LINQ.
Tags: , , ,
trackback

For those of us who are C# developers we are familiar with an anonymous type, defined as:

Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to first explicitly define a type. The type name is generated by the compiler and is not available at the source code level. The type of the properties is inferred by the compiler.

A neat feature, but once the anonymous object falls out of scope you no longer can use it.  Hugo Bonacci, creator of CSMongo and jLinq has created a class called AnonymousType that will allow you to persist an anonymous type beyond the scope of the method that creates that object.  In other words he has process that will allow to define an anonymous object on the fly such as:


AnonymousType.Create(new
 {
 Name = name,
 Department = dept
 });

This a bit of a paradigm shift, since you now can not access the object’s properties with a Getter: instead, you use a statement like:

string name = anonymousType.Get<int>("Name");

This is great but how would you query a List?  Certainly with AnonymousType as an object you may have a collection of these objects especially since this construct lends itself to being an all purpose DTO, or even function like a SharePoint list.

Querying a collection of AnonymousTypes is possible through the use of Expression Trees.  Using this technique you can query a list of AnonymousTypes with the following syntax:

var dept13 = agentList.AsQueryable()
                .Where(x => x.Has<int>("Department", Compare.Equal, 13);

Sensei’s solution is comprised of a Has() method on the AnonymousType class, and a static class AnonPredicates with Evaluate and CreateComparisonExpression methods.  Here’s the source project with unit tests.

AnonymousType.Has

public bool Has<T>(string propertyName, Compare comparesTo, object objectValue)
{
var value = this.Ge<T>(propertyName);

return AnonPredicate.Evaluate<T>(value, comparesTo, objectValue);
}

Has() is the starting point.  The first step is to get the current value of the property you wish to compare with the test value.  Since the properties of the AnonymousType are stored in a Dictionary you have to fetch that first.  The “T” of generic type will allow you to use the Has method for all types.  “T” will also tell the AnonymousType class the type that the property should be cast to.  This allows the class to determine what to do at runtime, and eliminates the need for you write methods per type, such as HasInt(), HasDouble(), HasString(), etc.

Has<T>() then passes the value, the type of comparison and the expected object to an evaluation method.

AnonPredicate.Evaluate


public static bool Evaluate<T>(T propertyValue, Compare comparesTo, object objectValue)
 {
 ParameterExpression parameter = Expression.Parameter(typeof(T), "x");

 Expression leftConstant = Expression.Constant(propertyValue, typeof(T));
 Expression rightConstant = Expression.Constant(objectValue, objectValue.GetType());

 var comparison = CreateComparisonExpression<T>(leftConstant, comparesTo, rightConstant);
 Expression<Func<T, bool>> predicate =
 Expression.Lambda<Func<T, bool>>(comparison, parameter);

 var execDelegate = predicate.Compile();
 return execDelegate(propertyValue);
 }

Evaluate will build an expression tree of the form “x == y”, where x is the value of the AnonymousType property as Constant, == is the comparison operation, and y is the compared value as Constant.  There is no dynamic look up of the AnonymousType property as we did that previously in the calling method Has().  Here we are just building a simple expression, and will determine if invoking that expression will yield a true or a false.

The parameter comparesTo is an Enumeration representing the equality expression to use:

public enum Compare
 {
 Equal = ExpressionType.Equal,
 NotEqual = ExpressionType.NotEqual,
 LessThan = ExpressionType.LessThan,
 GreaterThan = ExpressionType.GreaterThan,
 LessThanOrEqual = ExpressionType.LessThanOrEqual,
 GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,
 Contains = ExpressionType.TypeIs + 1,
 In = ExpressionType.TypeIs + 2
 }

Evaluate calls CreateComparisonExpression to build an equality expression.

CreateComparisonExpression

public static Expression CreateComparisonExpression<T>(Expression left, Compare comparesTo, Expression right)
 {
 switch (comparesTo)
 {
 case Compare.Equal:
 return Expression.Equal(left, right);

 case Compare.GreaterThan:
 return Expression.GreaterThan(left, right);

 case Compare.GreaterThanOrEqual:
 return Expression.GreaterThanOrEqual(left, right);

 case Compare.LessThan:
 return Expression.LessThan(left, right);

 case Compare.LessThanOrEqual:
 return Expression.LessThanOrEqual(left, right);

 case Compare.NotEqual:
 return Expression.NotEqual(left, right);

 case Compare.Contains:
 MethodInfo contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
 return Expression.Call(left, contains, right);

 case Compare.In:
 //  You are accepting a List&lt;T&gt;, where T corresponds to the property on your class
 //  I.E.  List&lt;int&gt; =&gt; Employee.Age as Integer.
 //  Comparison is Left Compares Right, hence you need the type of Left
 return Expression.Call(typeof(Enumerable), "Contains", new Type[] { left.Type }, right, left);

 default:
 throw new ArgumentException("Query.CreateComparisonExpression - comparison not supported");

 }

Here a simple switch is used to build and return our equality expression.  This is really a binary expression with a left and right constant coupled with the type of equality.

Take note of Contains and In at lines 23 and 27.  These use a bit reflection to call .Net methods to perform the comparison.  Contains will work for strings, while In works like the SQl In-clause and accepts an array of objects to test.  This eliminates the need for you to write multiple Or clauses and keeps your code more readable.

Back To Evaluate()

So far, we’ve accepted an enum that tells what operation to perform and have built and expression tree that will tell us is “x >= y” is true.  We build the Lambda expression with the line:

Expression<Func> predicate =
Expression.Lambda<Func>(comparison, parameter);

The last two line of Evaluate()  will execute the expression tree for us.  Before we execute we must compile the tree:

var execDelegate = predicate.Compile();

This makes the code executable. Next we run the code:

return execDelegate(propertyValue);

The code will yield a true or false, and we return this result.

Well, so what?

Remember our goal? We wanted to create a process that would examine the innards of an AnonymousType, return true when that property met a condition. This would let us do the following:

//  Find all agents in Department 13 or 21
int[] inList = new int[2]{13, 21};
var dept13_21 = agentList.AsQueryable()
                 .Where(x=> x.Has<int>("Department", Compare.In, inList));

Enough Wax-on / Wax-off.  Show me Crane Style!

You’ve done well Daniel-san.  Now apply your knowledge.  Even with all the syntax-sugar, what if you need to find all AnonymousTypes that have an Age property whose value is between 40 and 60?  Yes you could write this way:


var agentsAgeBetween40_60 = anonAgents.AsEnumerable()
 .Where(x => ( x.Has<int>("Age", Compare.GreaterThanOrEqual, 40) & (x.Has<int>("Age", Compare.LessThanEqual, 60)))
 .ToList();

But look at that mess!  If you don’t know karate you’ll have to learn it quick because your co-workers are going to hunt you down when they have to support this code.

Sensei’s shumatsu dosa ichi (“after class exercise number one”) is the Between method:


public bool Between<T>(string propertyName, T bottomRange, T topRange)
 {
 var value = this.Get<T>(propertyName);

 return ((AnonPredicate.Evaluate<T>(value, Compare.GreaterThanOrEqual, bottomRange))
 &amp; (AnonPredicate.Evaluate<T>(value, Compare.LessThanOrEqual, topRange)));
 }

Now we write our query as:

var salesAgesBetwee40_60 = agentList.AsQueryable()
.Where(x => x.Between("Age", 40, 60));

The Fung-Shui Kid Part 11

“Oh yeah, old man,” you say.  “How about karating this compound statement:  Show me all sales agents in between the ages of 30 and 40 whose first names begin with ‘St’!”

Before Sensei left the temple he gobbled down a dose of PredicateBuilder by Joe and Ben Albahari that allows him to create dynamic compound lambda expressions!  Shumatsu dosa ni (“after class exercise number two”) proceeds thusly:

public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                        Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                         Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;
              (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

So like Neo, now we don’t even have to dodge the bullets, as we’re writing this:


var predicate = PredicateBuilder.False<AnonymousType>();
 predicate = predicate.Or(x => x.Between<int>("Age", 40, 60));
 predicate = predicate.Or(x => x.Has<string>("Name", Compare.Contains, "St"));

 var salesAgentsBetwee40_60_STInName = agentList.AsQueryable()
 .Where(predicate);

The Return of the Sensei

Recapping all the action, we can create AnonymousTypes and access its properties in other calling methods.  In the middle of the fracas – or on the fly for you .Net types – when can create a class that serves as an anonymous type this can be passed to other methods.  As this lives as a regular object, we can create collections or lists of these objects and filter them using LINQ, Expression Trees, and the awesome PredicateBuilder.  Here are the source files.

Stay tuned – next time we leave the temple and hunt for beer!!  If you want to read more, gaze through these ancient scrolls from the masters:

Expression Tree Basics – Charlie Calvert’s Community Blog

Marc Gravell – Code, code and more code.: Explaining Expression

Marc Gravell – Code, code and more code.: Express yourself

Implementing Dynamic Searching Using LINQ (check the section regarding dynamic expressions.)

Comments»

1. movies online - October 5, 2010

Awesome website, I hadn’t come across activeengine.wordpress.com before in my searches!
Continue the great work!


Leave a comment