Add second group by key field to existing Lambda Expression

c# dynamic-linq lambda linq


I have a Group by expression that I am dynamically creating for use in a LINQ query. Currently, to construct the expression, I use the following code:

var arg = Expression.Parameter(typeof(T), helper.getName());
var prop = Expression.Property(arg, "customerType");
var body = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda<Func<Contact, object>>(body, arg);
var keySelector = lambda.Compile();

I then use the keySelector in the GroupBy for my LINQ query. My question is, if I wanted to add a second grouping criteria to this expression, say "salesStage", how would I add that to this existing expression?

10/22/2013 6:11:47 PM

Popular Answer

You have a problem, because what the compiler does on a regular GroupBy call is generate a new anonymous type with the properties you define. If the type doesn't exist, we cannot create an expression creating an object of the type.

However, given that you are using this for LINQ-to-Objects, we can use the Tuple<> type to generate the grouping key. Hopefully you do not need to group on more than 8 parameters.

Here is a generic function to generate the grouping function:

static Func<T, object> BuildGrouper<T>(IEnumerable<string> properties) {
    var arg = Expression.Parameter(typeof(T), helper.getName());
    // This is the list of property accesses we will be using
    var parameters = properties.Select(propName => Expression.Property(arg, propName)).ToList();
    // Find the correct overload of Tuple.Create.
    // This will throw if the number of parameters is more than 8!
    var method = typeof(Tuple).GetMethods().Where(m => m.Name == "Create" && m.GetParameters().Length == parameters.Count).Single();
    // But it is a generic method, we need to specify the types of each of the arguments
    var paramTypes = parameters.Select(p => p.Type).ToArray();
    method = method.MakeGenericMethod(paramTypes);
    // Invoke the Tuple.Create method and return the Func
    var call = Expression.Call(null, method, parameters);
    var lambda = Expression.Lambda<Func<T, object>>(call, arg);
    return lambda.Compile();
1/15/2014 4:27:02 PM

Related Questions

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow