Почему эти три части кода LINQ дают разные (или ошибочные) результаты? - PullRequest
5 голосов
/ 10 апреля 2011

Вот некоторые примеры данных:

List<Book> books = new List<Book>()
{
    new Book(){Title = "artemis fowl: the time paradox", Pages = 380},
    new Book(){Title = "the lieutenant", Pages = 258},
    new Book(){Title = "the wheel of time", Pages = 1032},
    new Book(){Title = "ender's game", Pages = 404},
    new Book(){Title = "the sphere",  Pages = 657}
};  

Справочная информация:

В приведенном выше примере используется упрощенная версия класса Book.Конечно, он будет содержать много полей.Моя конечная цель - дать пользователю возможность выполнить «расширенный» поиск, позволяющий ему указать любое поле, и дополнительно разрешить пользователю указывать ключевые слова, используя булеву алгебру для определенного поля.

Например: В текстовом поле поиска по названию: + (торт | выпечка) + ~ демон

Вышеприведенное означало бы: Найти все книги, в названии которых есть слова «торт», «любой из» торта.'или' выпечка 'и не имеет слова' демон '.

Проблема:

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

List<Func<Book, bool>> fs = new List<Func<Book, bool>>()
{
    b => b.Title.Contains("me"),
    b => b.Title.Contains("the")
};

var q2 = from b in books select b;
foreach (var f in fs)
    q2 = q2.Where(f);

foreach (Book b in q2)
{
    Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
                      b.Title, b.Pages);
}

Приведенный выше код работает нормально.Он ищет книги, в названии которых содержится «И» Я.

Фаза 2

Теперь приведенный выше фильтр имеет тип Func <<strong> Book, bool>.Этот класс будет сгенерированным классом Entity Framework, и я не хочу использовать его в своем слое пользовательского интерфейса, где будет вводиться поисковая фраза и будут создаваться поисковые фильтры для передачи в BLL.

Итак, у меня есть следующие три попытки:

var q = from b in books select b;

List<Func<string, bool>> filters  = new List<Func<string, bool>>()
{
    s => s.Contains("me"),
    s => s.Contains("the"),
};

//This works...
for (int i = 0; i != filters.Count; ++i)
{
    Func<string, bool> daF = filters[i];
    q = q.Where(b => (daF(b.Title)));
}     

            //This produces an exception...
            //Due to index in query?
//            for (int i = 0; i != filters.Count; ++i)
//            {
//                q = q.Where(b => ((filters[i])(b.Title)));
//            }

            //This runs but doesn't produce the proper output
//            foreach (Func<string, bool> filter in filters)
//              q = q.Where(b => filter(b.Title));

foreach (Book b in q)
{
    Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
                      b.Title, b.Pages);
}

Первая закомментированнаякусок запускает индексатор за исключением диапазона, заявляя, что значение i равно 2.

Второй закомментированный кусок запускается и выдает результат, но выводит ЧЕТЫРЕ из 5 книг ... все, кроме книгипод названием "игра Эндер".Это не правильно ...

Итак, читая мой пост, я вижу, что я не мог править в своей дурной привычке объяснять каждую мелочь ...

Итак, поехали.Пожалуйста, объясните, почему отличаются результаты.И я думаю, вы могли бы намекнуть на возможные улучшения моего текущего «решения».

1 Ответ

4 голосов
/ 10 апреля 2011

Поскольку мы используем здесь LINQ to Objects, вы должны иметь возможность использовать All().Тогда вам не понадобится циклпеременная цикла .Как правило, вы должны быть осторожны при использовании лямбда-функций в циклах из-за этого.Простое решение - объявить отдельную переменную, которую вы используете в своих лямбдах.Обратите внимание, что вы действительно сделали это косвенно в своем первом запросе.Однако вам вообще не нужны циклы, и вы должны использовать один из запросов выше.

for (int i = 0; i != filters.Count; ++i)
{
    var index = i;
    q = q.Where(b => filters[index](b.Title));
}

foreach (Func<string, bool> f in filters)
{
    var filter = f;
    q = q.Where(b => filter(b.Title));
}
...