使用多个字段构建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());