Tuesday, October 27, 2009

Expression Trees .NET 4.0 Part 3

This is the third part of a series of posts with regards to the new features of Expression Trees in .NET 4.0.  In this post I will try to make things a little bit more difficult than the previous two posts.  I will take an example from MSDN and I will try to make it work using the new features from .NET 4.0.  The main idea of this exercise is to use Expression Trees in order to build Dynamic Queries.  So the .NET 3.5 code is:

string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
"Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
"Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };

// The IQueryable data to query.
IQueryable<String> queryableData = companies.AsQueryable<string>();

// Compose the expression tree that represents the parameter to the predicate.
ParameterExpression pe = Expression.Parameter(typeof(string), "company");

// ***** Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) *****
// Create an expression tree that represents the expression 'company.ToLower() == "coho winery"'.
Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Expression right = Expression.Constant("coho winery");
Expression e1 = Expression.Equal(left, right);

// Create an expression tree that represents the expression 'company.Length > 16'.
left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16, typeof(int));
Expression e2 = Expression.GreaterThan(left, right);

// Combine the expression trees to create an expression tree that represents the
// expression '(company.ToLower() == "coho winery" || company.Length > 16)'.
Expression predicateBody = Expression.OrElse(e1, e2);

// Create an expression tree that represents the expression
// 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
// ***** End Where *****

// ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****

// Create an executable query from the expression tree.
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

// Enumerate the results.
foreach (string company in results)
Console.WriteLine(company);

/* This code produces the following output:

Blue Yonder Airlines
City Power & Light
Coho Winery
Consolidated Messenger
Graphic Design Institute
Humongous Insurance
Lucerne Publishing
Northwind Traders
The Phone Company
Wide World Importers
*/


In case you want to check the original post in MSDN check here



So now it is time to see how can we do the same thing using .NET 4.0.  I have created a small console application with the following code:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;

namespace ConsoleDynamic_Queries
{
class Program
{
static void Main(string[] args)
{
string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
"Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
"Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };

var items = FilterCompanies(companies, "coho winery");

foreach (string item in items)
Console.WriteLine("Items is: {0}", item);

Console.ReadLine();

}

private static List<string> FilterCompanies(string[] companies, string filter)
{
//Input Data
var inputValue = Expression.Parameter(typeof(string[]), "inputValue");
var filterValue = Expression.Parameter(typeof(string), "filterValue");
//Return Variable
var result = Expression.Variable(typeof(List<string>), "result");


var breakLoop = Expression.Label();
//Local Variables for the Loop part
var counter = Expression.Variable(typeof(int), "counter");
var arrayLength = Expression.Variable(typeof(int), "arrayLength");
var currentValue = Expression.Variable(typeof(string), "currentValue");

var process = Expression.Lambda<Func<string[], string, List<string>>>
(
Expression.Block
(
new[] { result},
Expression.Assign
(
result,
Expression.New(typeof(List<string>))
),
Expression.Block
(
new[] { counter, arrayLength, currentValue },
Expression.Assign
(
counter,
Expression.Constant(0)
),
Expression.Assign
(
arrayLength,
Expression.ArrayLength(inputValue)
),
Expression.Loop
(
Expression.Block
(
Expression.IfThen
(
Expression.IsTrue
(
Expression.Equal
(
counter,
arrayLength
)
),
Expression.Break(breakLoop)
),
Expression.Block
(
Expression.Assign
(
currentValue,
Expression.Call(Expression.ArrayAccess(inputValue, counter), typeof(string).GetMethod("ToLower", new Type[] { }))
),
Expression.Block
(
Expression.IfThenElse
(
Expression.GreaterThan
(
Expression.Property(currentValue, typeof(string).GetProperty("Length")),
Expression.Constant(16)
),
Expression.Call
(
result,
typeof(List<string>).GetMethod("Add"),
currentValue
),
Expression.IfThen
(
Expression.Equal
(
currentValue,
filterValue
),
Expression.Call
(
result,
typeof(List<string>).GetMethod("Add"),
currentValue
)
)
)
)

),
Expression.PostIncrementAssign(counter)
),
breakLoop
),
Expression.Block
(
Expression.Call
(
result,
typeof(List<string>).GetMethod("Sort", new Type[] {})
)
)
),
result
),
inputValue,
filterValue

).Compile();


return process(companies, filter);
}
}
}


So by running the code above I got exactly the same results with the .NET 3.5 implementation!!!  Keep in mind that for the second implementation you need to have VS2010 Beta 2 installed in your development environment.



I am not a smoker and this the SmokingCode!

No comments:

Post a Comment