Построение дерева выражений для динамической сортировки словаря словарей в LINQ - PullRequest
4 голосов
/ 30 сентября 2010

Я пытаюсь динамически создать дерево выражений, чтобы изменить порядок сортировки данных, содержащихся в словаре словарей. Существует много информации о динамическом указании столбца для сортировки, но это не та часть, с которой у меня возникают проблемы. Я борюсь с 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, чтобы динамически сортировать этот словарь словарей?

Спасибо
Jason

1 Ответ

4 голосов
/ 30 сентября 2010

Вы можете написать

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]);
    }        
}   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...