构建表达式树以在LINQ中动态排序字典字典

时间:2010-09-30 14:35:30

标签: c# lambda

我正在尝试动态构建表达式树,以便我可以更改字典字典中包含的数据的排序顺序。有很多关于动态指定要排序的列的信息,但这并不是我遇到问题的部分。我正在努力构建我的表达式树的 MethodCallExpression

出于这个例子的目的,我简化了字典:

Dictionary<string, Dictionary<int, int>> data = new Dictionary<string, Dictionary<int, int>>();

我正在尝试构建一个类似于以下内容的表达式:

data.OrderByDescending(someValue)
    .ThenByDescending(someothervalue)
    .ThenByDescending(anothervalue)...etc

在运行时确定'ThenBy'或'ThenByDescending'子句的数量。

让我们说一个例子需要按键4排序,然后是3,然后是1.我已经建立(我认为)以下表达式转换为我的3个排序顺序:

Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex1 = (r => r.Value[4]);
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex2 = (r => r.Value[3]);
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex2 = (r => r.Value[1]);

所以在编译时我可以编写这个表达式并且工作正常:

var sortedResults = dic.OrderByDescending(ex1.Compile()).ThenByDescending(ex2.Compile()).ThenByDescending(ex3.Compile());

然而,由于排序表达式的数量在运行时会有所不同,我需要动态构建它,这是我正在努力的地方。 我知道可以使用MethodCallExpression在运行时构建查询表达式。 MSDN示例显示了这一点:

    // ***** OrderBy(company => company) *****
    // Create an expression tree that represents the expression
    // 'whereCallExpression.OrderBy(company => company)'
    MethodCallExpression orderByCallExpression = Expression.Call(
        typeof(Queryable),
        "OrderBy",
        new Type[] { queryableData.ElementType, queryableData.ElementType },
        whereCallExpression,
        Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
    // ***** End OrderBy *****

但是,我无法从这个例子转换到使用它的词典词典:

Func<KeyValuePair<string, Dictionary<int, int>>, int>

我认为我需要做的是写这样的东西(这是部分伪代码):

    private static void Test()
    {
        var query = data.AsQueryable()
        foreach (int key in ListOfRequiredKeys)
        {
            Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> exp = (r => r.Value[key]);
            MakeQuery(exp, query);
        }        
    }   

    private static IQueryable MakeQuery(Expression<Func<KeyValuePair<string, Dictionary<int, int>> exp, IQueryable query)
    {
        MethodCallExpression orderByCallExpression = Expression.Call(
        typeof(Queryable),
        "ThenBy",
        new Type[] { query.ElementType, query.ElementType },
        query.Expression,
        Expression.Lambda<Expression<Func<KeyValuePair<string, Dictionary<int, int>>>(exp));
    }

我知道这不是正确的语法,但它应该表明我的想法。有人可以建议如何从MSDN示例移动到动态排序这个字典词典吗?

感谢
杰森

1 个答案:

答案 0 :(得分:4)

你可以写

var result = data.OrderByDescending(someValue)
                 .ThenByDescending(someothervalue)
                 .ThenByDescending(anothervalue); //...etc

作为

var result = data.OrderByDescending(someValue);
result = result.ThenByDescending(someothervalue);
result = result.ThenByDescending(anothervalue);
//...etc

因此,您只需致电OrderBy(Descending)即可获得IOrderedEnumerable/Queryable,然后可以重复调用ThenBy(Descending)

private static void Test()
{
    var query = data.AsQueryable();

    var f = ListOfRequiredKeys.First();
    var orderedQuery = query.OrderBy(r => r.Value[f]);

    foreach (int key in ListOfRequiredKeys.Skip(1))
    {
        var k = key;
        orderedQuery = orderedQuery.ThenBy(r => r.Value[k]);
    }        
}