使用LINQ构建表达式树以动态排序字典中的字典。

4

我正在尝试动态构建表达式树,以便可以更改包含在字典中的字典数据的排序顺序。有很多关于动态指定要排序的列的信息,但这并不是我遇到问题的部分。我在处理构建表达式树的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排序的一个例子。我已经确定(我认为)以下表达式可以翻译成我的三个排序顺序:

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个回答

4

您可以编写

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

as

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]);
    }        
}   

谢谢你。我花了很长时间才写完那篇文章,而你只用了几秒钟就回答了它... - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接