使用多个字段构建GroupBy表达式树

要动态生成GroupBy表达式,我正在尝试构建Linq表达式树。 要分组的字段是动态的,可以在数量上有所不同。

我用这个代码:

string[] fields = {"Name", "Test_Result"}; Type studentType = typeof(Student); var itemParam = Expression.Parameter(studentType, "x"); var addMethod = typeof(Dictionary).GetMethod( "Add", new[] { typeof(string), typeof(object) }); var selector = Expression.ListInit( Expression.New(typeof(Dictionary)), fields.Select(field => Expression.ElementInit(addMethod, Expression.Constant(field), Expression.Convert( Expression.PropertyOrField(itemParam, field), typeof(object) ) ))); var lambda = Expression.Lambda<Func<Student, Dictionary>>( selector, itemParam); 

代码是从这篇文章复制而来的 (谢谢Mark Gravel!)。

最终确定……

 var currentItemFields = students.Select(lambda.Compile()); 

……我预计我可以把它改成……

 var currentItemFields = students.GroupBy(lambda.Compile()); 

我认为lambda表达只不过是……

 var currentItemFields = students.GroupBy(o => new { o.Name, o.Test_Result }); 

……但不幸的是,情况似乎并非如此。 具有动态lambda的GroupBy不会给出任何exception,它只是不对任何内容进行分组并返回所有元素。

我在这做错了什么? 任何帮助,将不胜感激。 提前致谢。

该lambda表达式构建了分组字段的字典。
Dictionary不实现Equals()GetHashCode() ,因此它通过引用相等性对它们进行分组。
由于您总是返回一个新字典,因此每个项目都有自己的组。

您需要更改它以创建一个正确实现Equals()GetHashCode()以获得值相等的类型。
通常,您可以让编译器生成匿名类型。 但是,您不能在此处执行此操作,因为您在编译时不知道类型签名。
相反,你可以构造一个Tuple<...>

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

这篇文章展示了一个表达式函数,可以用于Select和GroupBy。 希望它能帮助别人!

 public Expression> GroupByExpression(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>(body, param); return expr; } 

被称为这样:

 var lambda = GroupByExpression(fields); var currentItemFields = students.GroupBy(lambda.Compile());