Build GroupBy expression tree with multiple fields

前端 未结 2 589
被撕碎了的回忆
被撕碎了的回忆 2020-12-06 03:21

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 co

相关标签:
2条回答
  • 2020-12-06 03:59

    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());
    
    0 讨论(0)
  • 2020-12-06 04:08

    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))
    )
    
    0 讨论(0)
提交回复
热议问题