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.
This post shows a expression function which can be used for both Select and GroupBy. Hope it helps others!
public Expression<Func<TItem, object>> GroupByExpression<TItem>(string[] propertyNames)
{
var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray();
var propertyTypes = properties.Select(p => p.PropertyType).ToArray();
var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length);
var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes);
var constructor = tupleType.GetConstructor(propertyTypes);
var param = Expression.Parameter(typeof(TItem), "item");
var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p)));
var expr = Expression.Lambda<Func<TItem, object>>(body, param);
return expr;
}
To be called like this:
var lambda = GroupByExpression<Student>(fields);
var currentItemFields = students.GroupBy(lambda.Compile());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With