Нужна помощь с добавлением предложений Где в цикле с Linq - PullRequest
2 голосов
/ 17 декабря 2009

Ниже приведен пример грубого и упрощенного кода того, что я пытаюсь сделать, и он работает не так, как ожидалось. Я использую Linq для Entity в случае, если это имеет значение.

По сути, первый цикл с DateClause заканчивается тем, что ничего не возвращает, если у меня есть два предложения date, отлично работает с одним предложением date. В цикле TextClause он, кажется, игнорирует каждое текстовое предложение после первого.

Я ожидаю, что эти предложения будут объединены AND, поэтому, если я сделаю текстовое предложение, а затем сделаю другое, они должны быть добавлены, и я получу более сфокусированные результаты, особенно с использованием метода Contains.

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

Я уверен, что делаю что-то не так или просто глупо, но не вижу этого.

enum Operators
{
    Contains,
    DoesNotContain,
    GreaterThan,
    LessThan
}

public void DoSomething(List<DateClauses> dateClauses, List<TextClauses> textClauses)
{
    var query = from t in context.Table
                where t.Enabled = true
                select new
                {
                    title = t.title
                    date = t.date
                }


    foreach (DateClause clause in dateClauses)
    {
        switch (clause.Operator)
        {
            case Operator.GreaterThan:
                query = query.Where(l => l.date > clause.Date);
                break;
            case Operator.LessThan
                query = query.Where(l => l.date < clause.Date);
                break;
        }       
    }

    foreach (TextClause clause in textClauses)
    {
        switch (clause.Operator)
        {
            case Operator.Contains:
                query = query.Where(l => l.title.Contains(clause.Text));
                break;
            case Operator.DoesNotContain
                query = query.Where(l => !l.title.Contains(clause.Text));
                break;
        }
    }
}

РЕДАКТИРОВАТЬ: обновленный пример кода, чтобы показать использование enum в процессе. Когда я экстраполировал использование перечисления на некоторые из очень хороших решений (которые иначе работали бы с bool), я вызвал исключение, указанное в комментарии от меня в ответе Джоэла.

Я хочу сказать, что мне нравится то, что я видел в ответах, и я выучил несколько новых трюков с Linq, извиняюсь за пример, так как не думал, что bool vs enum будет иметь большое значение для Linq. Я надеюсь, что изменения помогут найти решение, которое может работать на меня. Еще раз спасибо за замечательные ответы на сегодняшний день.

Ответы [ 3 ]

4 голосов
/ 17 декабря 2009

Просто дикое предположение, но может ли быть так, что изменение ваших циклов на следующее будет иметь разные результаты?

foreach (DateClause clause in dateClauses)
{
    var capturedClause = clause;
    switch (clause.Operator)
    {
            case Operator.GreaterThan:
                    query = query.Where(l => l.date > capturedClause.Date);
                    break;
            case Operator.LessThan
                    query = query.Where(l => l.date < capturedClause.Date);
                    break;
    }               
}

Вы создаете, где критерии захватывают переменную цикла, а не значение переменной цикла для каждой итерации. Таким образом, в конце каждое Where будет одинаковым, используя значение для последней итерации цикла, так как запрос выполняется после последней итерации. Вводя временную переменную, вы будете захватывать переменную для каждой итерации.

См. Также блог Эрика Липперта на эту тему .

2 голосов
/ 17 декабря 2009
 var query = context.Table
         .Where(t => t.Enabled 
             && textClauses.Where(c => c.Contains).All(c => t.title.Contains(c.Text) )
             && textClauses.Where(c => c.DoesNotContain).All(c => !t.title.Contains(c.Text) )
             && dateClauses.Where(c => c.GreaterThan).All(c => t.date > c.Date) )
             && dateClauses.Where(c => c.LesserThan).All(c => t.date < c.Date) )
          ).Select(t =>  new {
               title = t.title
               date = t.date
      });

Главное здесь в том, что каждый из ваших текущих foreach циклов может быть реорганизован для использования .All() вместо этого. Или, скорее, каждое if условие внутри цикла может быть. Если вы действительно хотите, вы все равно можете разорвать каждое из где:

var query = context.Table.Where(t => t.Enabled).Select(t =>  new {
               title = t.title
               date = t.date
      });

query = query.Where(t => textClauses.Where(c => c.Contains).All(c => t.title.Contains(c.Text) );
query = query.Where(t => textClauses.Where(c => c.DoesNotContain).All(c => !t.title.Contains(c.Text) );
query = query.Where(t => dateClauses.Where(c => c.GreaterThan).All(c => t.date > c.Date) );
query = query.Where(t => dateClauses.Where(c => c.LesserThan).All(c => t.date < c.Date) );
0 голосов
/ 17 декабря 2009

Я делал это раньше без проблем - возможно, вам следует попробовать переместить .Выбрать до конца (после завершения Wheres). Кроме того, я в значительной степени использую PredicateBuilder для дополнительной гибкости при работе с динамическими запросами - сейчас вы получаете "и" семантику для ваших добавленных областей, где, я думаю, вы хотите "или" семантику хотя бы для некоторых из них. PredicateBuilder позволяет вам сделать это легко (и это бесплатно!).

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