我想使用C#中的表达式树创建以下lambda表达式:
var result = dataList.GroupBy(x => new { x.Prop1, x.Prop2 })
如何使具有两个属性的匿名类型作为LINQ表达式(lambdaExp)?
这是我到目前为止得到的:
IQueryable<GraphData> queryableData = graphDataList.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(GraphData), "x");
Expression prop1 = Expression.PropertyOrField(pe, "Prop1");
Expression prop2 = Expression.PropertyOrField(pe, "Prop2");
var lambdaExp = Expression.Lambda<Func<GraphData, object>>( new { prop1, prop2 } , pe); //doesn't compile
MethodCallExpression groupByCallExpression = Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { typeof(GraphData), typeof(object) },
queryableData.Expression,
lambdaExp);
IQueryable<GraphData> result = queryableData.Provider.CreateQuery<GraphData>(groupByCallExpression);
答案 0 :(得分:0)
当您编写linq查询和匿名对象时,编译器隐藏了许多对您有用的魔术。特别是对于匿名对象,它还为您创建新类型。除非该类型存在于某处,否则您将需要手动创建该类型并在其位置使用该类型。
您可以作弊,并让编译器创建具有该类型的对象并保存对该类型的引用。这样,您可以生成必要的表达式以实例化该对象。
要注意的一件事是,为您创建的匿名对象将具有按定义顺序包含参数的构造函数,因此您只需要调用该构造函数即可。
var keyType = new { Prop1=default(string), Prop2=default(string) }.GetType();
var ctor = keyType.GetConstructor(new Type[] { typeof(string), typeof(string) });
var param = Expression.Parameter(typeof(GraphData), "x");
var keySelector = Expression.Lambda(
Expression.New(ctor,
Expression.PropertyOrField(param, "Prop1"), // corresponds to Prop1
Expression.PropertyOrField(param, "Prop2") // corresponds to Prop2
),
param
); // returns non-generic LambdaExpression
请记住,我们正在处理编译时未知的类型,因此您的键选择器表达式将没有编译时类型。