如何将匿名对象创建为LINQ表达式

时间:2018-10-18 12:24:34

标签: c# linq lambda expression

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

1 个答案:

答案 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

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