C# Dynamic LINQ: выберите с суммой в индексаторе словаря

Я использую динамический LINQ для создания группы и выбора на лету. Мои элементы представляют собой коллекции ключей/значений (словари), поэтому они не содержат свойств (это требование дизайна и не может быть изменено). Мне удалось решить часть groupby в еще один вопрос, но он не работает в методе выбора.

Мой код ниже:

    private void GetValuesGroupedBy(List<Dictionary<string, object>> list, List<string> groupbyNames, List<string> summableNames)
    {
        // build the groupby string
        StringBuilder groupBySB = new StringBuilder();
        groupBySB.Append("new ( ");
        bool useComma = false;
        foreach (var name in groupbyNames)
        {
            if (useComma)
                groupBySB.Append(", ");
            else
                useComma = true;

            groupBySB.Append("it[\"");
            groupBySB.Append(name);
            groupBySB.Append("\"]");
            groupBySB.Append(" as ");
            groupBySB.Append(name);
        }
        groupBySB.Append(" )");

        // and now the select string
        StringBuilder selectSB = new StringBuilder();
        selectSB.Append("new ( ");
        useComma = false;
        foreach (var name in groupbyNames)
        {
            if (useComma)
                selectSB.Append(", ");
            else
                useComma = true;

            selectSB.Append("Key.")
                //.Append(name)
                //.Append(" as ")
                .Append(name);
        }
        foreach (var name in summableNames)
        {
            if (useComma)
                selectSB.Append(", ");
            else
                useComma = true;

            selectSB.Append("Sum(")
                .Append("it[\"")
                .Append(name)
                .Append("\"]")
                .Append(") as ")
                .Append(name);
        }
        selectSB.Append(" )");

        var groupby = list.GroupBy(groupBySB.ToString(), "it");
        var select = groupby.Select(selectSB.ToString());
    }

Ключевая часть строки выбора в порядке, но часть Sum не работает. Предполагая, что ключ, который я хочу, называется значением, я пробовал:

  • «Сумма (значение)» : ParseException: ожидается выражение

  • "Sum(\"value\")" : ParseException: ожидается выражение

  • "Sum(it[\"value\"])" : ParseException : не существует применимого статистического метода "Sum"

  • «Сумма (это [значение])»: ParseException: в типе «Словарь» не существует свойства или поля «значение».

  • "Sum([\"value\"])" : ParseException: ожидается выражение

Но все потерпели неудачу. Есть идеи?

Спасибо! Шон


person sean.net    schedule 30.05.2012    source источник
comment
ИМО, чем дальше в кроличью нору вы заберетесь с динамическим LINQ, тем больше вам следует подумать о том, чтобы самостоятельно освоить артикуляцию деревьев выражений.   -  person Kirk Woll    schedule 30.05.2012


Ответы (2)


Я сам столкнулся с этой проблемой; проблема в том, что Sum() видит столбец как тип object (который может быть применен к Convert() или даже к Max(), но не к Sum()), и поэтому не работает; обратите внимание, что в нем говорится, что нет применимых агрегатных функций.

Решение состоит в том, чтобы использовать встроенное преобразование в целое число. Dynamic LINQ поддерживает это преобразование, и в вашем примере это можно сделать следующим образом:

selectSB.Append("Sum(")
    .Append("Convert.ToInt32(") // Add this...
    .Append("it[\"")
    .Append(name)
    .Append("\"]")
    .Append(")") // ... and this
    .Append(") as ")
    .Append(name);

Если ваши столбцы не относятся к типу int, я считаю, что ToInt64 (bigint) работает, а также ToDouble() и ToDecimal().

person Eric    schedule 05.06.2013

У вас правильный синтаксис (мои тестовые поля "Один" и "Два")

new ( Key.One, Sum(it["Two"]) as Two )

Однако нет метода linq Sum(object), вам нужно указать, что это за суммирование (int, float, decimal и т.д.)

Самый простой способ — изменить определение словаря на Dictionary‹'string, int> при условии, что вы суммируете целые числа.

person Adam Mills    schedule 29.06.2012