Aggregation-Functions in an Expression Tree

J

Jan Rehm

Hello,
I'm currently trying to implement an extension of the DynamicQueryable class
in order to add aggregation functions (Sum, Min, Max, AVG).

The goal is to be able to use these functions to get a dynamically generated
SQL-Expression.

As the database is filled with "null" and "string", a Lambda-Expression is
needed to convert these values into a useable format.

Trial and error with static Linq-Queries have brought me to this query:

var query = dxStatic.T_OBJ11s.Sum(g => g.F13 == null ? 0 :
Convert.ToDouble(g.F13));

with dxStatic being a System.Data.Linq.DataContext to connect to the Database.

This query produces the expected values from the database.

Then, I tried to translate this query into an ExpressionTree, in order to be
able to further refine/change the query during runtime.

This is where I hit a snag:

Creating the Lambda-Expression was comparatively easy:

private static LambdaExpression ConvertTable(IQueryable source, string key)
{
// Source is once again Syste.Data.Linq.DataContext

// g
ParameterExpression TableObj =
Expression.Parameter(source.ElementType, "g");

// g.key
MemberInfo Key = source.ElementType.GetMember(key)[0];
MemberExpression Column = Expression.MakeMemberAccess(TableObj, Key);

// null, 0
ConstantExpression Null = Expression.Constant(null);
ConstantExpression Zero = Expression.Constant((decimal)0,
typeof(decimal));

// g.key == null
Expression query = Expression.Equal(Column, Null);

// Convert.ToDecimal(g.key)
MethodInfo ConverterMethod = typeof(Convert).GetMethod("ToDecimal",
new Type[] { typeof(string) });
Expression convert = Expression.Call(ConverterMethod, Column);

// g.key==null ? 0 : Convert.ToDecimal(g.key)
Expression decider = Expression.Condition(query, Zero, convert);

// g => (g.key==null ? 0 : Convert.ToDecimal(g.key))
LambdaExpression ConvertOrNull = Expression.Lambda(decider, TableObj);


return ConvertOrNull;
}


The LambdaExpression can then be used:

public static IQueryable Sum(this IQueryable source, string key, params
object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (key==null) throw new ArgumentNullException("key");

// g => (g.key==null ? 0 : Convert.ToDecimal(g.key))
LambdaExpression ConvertOrNull = ConvertTable(source, key);

Expression SumExpression = Expression.Call(typeof(Queryable), "Sum",
new Type[] { source.ElementType }, source.Expression,
Expression.Quote(ConvertOrNull));

return source.Provider.CreateQuery(SumExpression);
}

This fails, due to an error in CreateQuery. Further research with .NET
Reflector led to this:

Type type = TypeHelper.FindGenericType(typeof(IQueryable<>),
expression.Type);
if (type == null)
{
throw Error.ArgumentNotValid("expression");
}

As type is always null, FindGenericType obviously can't find a generic type.

I've tried a lot of different approaches to solve this Problem, but I'm
stumped. Can anybody help?

Thank you.

-Jan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top