Построение запроса LINQ программно без локальных переменных, обманывающих меня
Предположим, что мои объекты находятся в идеальном рабочем состоянии (т.е. TDD заставляет меня думать, что они работают).
У меня есть список, который я создаю как это (за исключением отступов):
var result = from v in vendors
from p in v.Products
orderby p.Name
select p;
Это работает - я получаю все продукты от всех поставщиков.
Теперь у меня есть список условий, созданных пользователем во время выполнения. Пусть они применяются:
foreach (Attribute a in requiredAttributes)
{
result = result.Where(p => p.Attributes.Contains(a));
}
Это может быть примитивно, но я думал, что это сработает. Однако после завершения цикла foreach, когда вы перечисляете "результат", он будет содержать все продукты, у которых есть атрибут LAST из коллекции необходимых атрибутов в нем свойство Attributes (также коллекция).
Для меня это пахнет как "а", где-то переписывается с каждой поездкой через цикл, и применяется только последний.
Короче говоря, как-то написать метод расширения для IEnumerable, называемый ContainsAll (IEnumerable) или что-то в этом роде, как я могу достичь того, что хочу, что в основном логическое И, предоставляя мне только те продукты, которые имеют ВСЕ требуемые атрибуты?
Ответы
Ответ 1
(Отредактировано для наглядности.)
Проблема - это цикл foreach и тот факт, что переменная "a" захватывается, а затем изменяется каждый раз. Здесь будет модификация, которая будет работать, эффективно вводя "новую" переменную для каждой итерации цикла и захватывая эту новую переменную.
foreach (Attribute a in requiredAttributes)
{
Attribute copy = a;
result = result.Where(p => p.Attributes.Contains(copy));
}
Решение Omer является более чистым, если вы можете его использовать, но это может помочь, если ваш реальный код на самом деле сложнее:)
РЕДАКТИРОВАТЬ: здесь больше о проблеме в это закрывает статью - прокрутите вниз до "Сравнение стратегий захвата: сложность и мощность".
Ответ 2
var result = from v in vendors
from p in v.Products
where requiredAttributes.All(a => p.Attributes.Contains(a))
orderby p.Name
select p;
НТН.
Ответ 3
Я не закодировал его, но изменил
foreach (Attribute a in requiredAttributes){
result = result.Where(p => p.Attributes.Contains(a));
}
к
foreach (Attribute a in requiredAttributes){
Attribute b = a;
result = result.Where(p => p.Attributes.Contains(b));
}
тоже должен исправить это.