3

Is there any way how to Group By multiple columns dynamically?

Eg. group x by new { x.Column1, x.Column2 }

but the x.Column1 etc. I want to set dynamically (from UI)

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • check [this post](http://stackoverflow.com/questions/3929041/dynamic-linq-groupby-multiple-columns) – Yuliam Chandra Jul 23 '14 at 10:03
  • Thanks, the post above shows two ways. The first one using regular LINQ does not seem to work properly and seems to be too ugly to solve such a elementary problem. The second one using Dynamic LINQ may be the way. Is there any other simple way how to solve this simple challenge? – Petr Kaněčka Jul 23 '14 at 11:09
  • Not I know of, either build expression tree or use dynamic linq. – Yuliam Chandra Jul 23 '14 at 11:21
  • And that is the question, how to build an expression tree for "group by new {}" syntax? – Petr Kaněčka Jul 23 '14 at 12:46

1 Answers1

0

The way to achieve this dynamically on db site is quite complicated as we cannot dynamically create anonymous types. To replace them I would suggest to create a class:

public class CustomTuple<T1, T2>
{
    public T1 Item1 { get; set; }
    public T2 Item2 { get; set; }
}

We cannot use Tuple here as it does not have default constructor. In CustomTuple class place as much parameters T and as much properties as you would need at max. If you will define in that class 5 properties but for the query you will use only 3 you just set only 3 properties to proper values and the remaining 2 properties you keep null - the query will still work. Alternatively you may dynamically at run time generate proper class with CodeDOM. Then comes query logic:

Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
    typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
    Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
    Expression.MemberInit(
        body,
        binding1);

Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
    var list = context.Entities.GroupBy(exp).ToList();
}

The above code groups Entities by Value property. parameterTypes may be dynamically build during program execution - this is list of types of properties anonymous type for key selection in group by would have. Basing on that we create proper CustomTuple type. Then we dynamically create at run time binding1 elements - one per each property to be set for grouping key. In the example above I create only one. With use of the NewExpression and MemberBinding expression we may build initialization expression with MemberInit method. Finally you build lambda expression from that and execute it against db.

mr100
  • 4,340
  • 2
  • 26
  • 38