Build GroupBy expression tree with multiple fields

c#, expression-trees, lambda, linq

Solution

That lambda expression builds a dictionary of grouping fields. `Dictionary<TKey, TValue>` does not implement `Equals()` and `GetHashCode()`, so it groups them by reference equality. Since you always return a new dictionary, each item gets its own group.

You need to change it to create a type that correctly implements `Equals()` and `GetHashCode()` for value equality. Ordinarily, you would have the compiler generate an anonymous type. However, you can't do that here since you don't know the type signature at compile-time. Instead, you can construct a `Tuple<...>`:

Expression.New(
    Type.GetType("System.Tuple`" + fields.Length)
        .MakeGenericType(fields.Select(studentType.GetProperty), 
    fields.Select(f => Expression.PropertyOrField(itemParam, f))
)

Problem

To dynamically generate a GroupBy expression, I am trying to build a Linq expression tree. The fields to group by are dynamic and can differ in number. I use this code: ``` string[] fields = {"Name", "Test_Result"}; Type studentType = typeof(Student); var itemParam = Expression.Parameter(studentType, "x"); var addMethod = typeof(Dictionary<string, object>).GetMethod( "Add", new[] { typeof(string), typeof(object) }); var selector = Expression.ListInit( Expression.New(typeof(Dictionary<string,object>)), fields.Select(field => Expression.ElementInit(addMethod, Expression.Constant(field), Expression.Convert( Expression.PropertyOrField(itemParam, field), typeof(object) ) ))); var lambda = Expression.Lambda<Func<Student, Dictionary<string,object>>>( selector, itemParam); ``` The code is copied from this post (Thanks Mark Gravel!). It finalizes with ... ``` var currentItemFields = students.Select(lambda.Compile()); ``` ... of which I expected that I could change it to ... ``` var currentItemFields = students.GroupBy(lambda.Compile()); ``` I assumed that the lambda expression is nothing more than ... ``` var currentItemFields = students.GroupBy(o => new { o.Name, o.Test_Result }); ``` ... but unfortunally that seems not to be the case. The GroupBy with a dynamic lambda does not give any exceptions, it just doesn't group anything and returns all elements. What am I doing wrong here? Any help would be appreciated. Thanks in advance.

Original source

Related problems