Построение запроса 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));
}

тоже должен исправить это.