Создание динамических запросов LINQ с использованием деревьев выражений - PullRequest
0 голосов
/ 05 февраля 2019

У меня проблемы с возможностью репликации нескольких .Where в моем запросе linq с помощью деревьев выражений. Мне нужно, чтобы этот запрос был динамическим, поскольку он является функцией поиска.эффективный и нефильтрованный фильтр на стороне клиента.Я, по крайней мере, пытаюсь начать с фильтрации по 3 из этих столбцов.Ниже я добавил три идентификатора, которые я пытаюсь отфильтровать.Я много читал в деревьях выражений, но мог бы использовать кого-то, чтобы фактически применить их к тому, что я делаю, потому что они сбивают меня с толку.

    public int? heightId { get; set; }
    public int? ageId { get; set; }
    public int? genderId { get; set; }

Ответы [ 2 ]

0 голосов
/ 05 февраля 2019

Итак, у вас есть класс (или несколько классов) с ограниченным количеством свойств, вероятно, меньше 100.

Где-то в вашем коде это может быть в пользовательском интерфейсе, но также может бытьпосле прочтения интернет-страницы, файла или получения информации из базы данных, где-то в вашем коде у вас будет некоторый ввод, из которого вы можете решить, какой Where должен быть выполнен.

Итак, где-то в вашем коде, у вас есть функция, которая принимает некоторые входные данные и возвращает выражение вашего где:

Expression<Func<MyTable, bool>> CreateExpression(MyInput input) {...}

Проблема в том, что мы не знаем, как вы получаете ввод.Что мы знаем, так это то, что существует только ограниченный (меньше, чем ранее упомянутый 100) возможный результат.Если есть ошибочные входные данные, то процедура должна либо исправить это в лучший вывод догадки, либо отказаться от создания выходных данных.

В любом случае, эта длинная история должна сказать вам, что код, который должен создавать выражение,относительно ограничен.Это гораздо понятнее, тестируемый , поддерживаемый, если вы не сделаете его динамическим, а создадите нединамическую функцию, даже если у вас большой оператор switch с 50 случаями.

Дляпример: предположим, что оператор должен ввести имя и значение свойства, по которому он хочет фильтровать.Он может совершать ошибки при наборе, и в этом случае вы предупреждаете оператора об ошибке при наборе

Expression<Func<MyTable, bool>> CreateExpression(string input, int value)
{
     // TODO: make the procedure case insensitive
    switch (input)
    {
        case "HeightId":
             return myItem => myItem.HeightId == value;
        case "AgeId":
             return myItem => myItem.AgeId == value;
        ...
        default:
            WarnUserIncorrectInput(...)
            return null;
    }
}

Сколько случаев вы ожидаете?Даже если их будет 20, этот код будет намного проще для понимания и проверки на ошибочный ввод, чем если бы вы использовали магическую интерпретацию для создания своего выражения.В конце концов: эта магическая интерпретация также должна проверять наличие ошибочного ввода.

Если в будущем свойство будет добавлено или удалено, то эту функцию будет довольно легко поддерживать.

Сложные выражения

Возможно, вы хотите, чтобы оператор комбинировал выражения с использованием некоторого определенного языка, например:

"HeightId == 4 AND AgeId == 10 OR ..."

Если вы выберете такой тип ввода,Вы делаете свою жизнь очень трудной.Для этого вам почти придется создать какой-то компилятор, проверяющий все виды ошибочных входных данных.Вам нужно будет определить, когда оператор имеет в виду свойство, а когда он означает операцию или компаратор.Это нелегко сделать.

Если вы действительно хотите иметь возможность комбинировать выражения, рассмотрите возможность использования элементов пользовательского интерфейса, таких как поля со списком, где пользователи должны выбрать свойство и ввести правильный тип значения.

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

ExpressionFunc<TSource, bool>> CreateAnd<TSource>(
    Expression<Func<TSource, bool>> X,
    Expression<Func<TSource, bool>> Y)
{
     return (sourceElement) => X(sourceElement) && Y(sourceElement);
}

Опять же: даже если вы хотите позволить оператору комбинировать операторы, если вы используете комбинированные поля или аналогичные, вы не можете получить неправильные входные данные и числокомбинации также довольно ограничены.

Заключение

Хотя можно создать выражение из строкового ввода, это будет означать, что вам придется создать компиляторкак вещь, которая проверяет на ошибочные входные данные.Это было бы непросто понять, протестировать, поддерживать.

Поскольку количество запрашиваемых операторов довольно ограничено, возможно, менее 100, рассмотрите возможность использования функции с большим регистром переключателей

0 голосов
/ 05 февраля 2019

Это не динамично, потому что вы знаете столбцы заранее.Вам также не нужно использовать деревья выражений.Вы можете просто применить фильтры условно:

public Class[] FindByFilter(int limit, int? heightId = null, int? ageId = null, int? genderId = null)
{
    var classes = databaseContext.Set<Class>();

    if (heightId.HasValue)
        classes = classes.Where(c => c.HeightId == heightId.Value);

    if (ageId.HasValue)
        classes = classes.Where(c => c.AgeId == ageId.Value);

    if (heightId.HasValue)
        classes = classes.Where(c => c.GenderId == genderId.Value);    

    return classes.Take(limit).ToArray();
}

Итак, теперь FindByFilter(10, 1, null, 2) или эквивалент FindByFilter(10, heightId: 1, genderId: 2) вернет ТОП 10 строк с ростом 1, полом 2, но с любым возрастом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...