首页 > 解决方案 > 如何创建匿名对象作为 LINQ 表达式

问题描述

我想使用 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);

标签: c#linqlambdaexpression

解决方案


当您编写 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

请记住,我们正在处理编译时未知的类型,因此您的键选择器表达式将没有编译时类型。


推荐阅读