我正在尝试动态构建一个表达式树,以便可以更改字典中包含的数据的排序顺序。有很多关于动态指定要排序的列的信息,但这并不是我真正遇到的问题。我正在与构建表达式树的MethodCallExpression进行斗争。
在本例中,我简化了字典:

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

我试图建立一个类似这样的表达式:
data.OrderByDescending(someValue)
    .ThenByDescending(someothervalue)
    .ThenByDescending(anothervalue)...etc

其中“thenby”或“thenbydranceding”子句的数目是在运行时确定的。
举一个例子,需要按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>

我想我需要做的是这样写(这是部分psuedo代码):
    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示例转移到动态地对字典进行排序吗?
谢谢
杰森

最佳答案

你可以写

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

09-10 03:20
查看更多