Как применить несколько условий фильтра (одновременно) к списку?

У меня есть следующий код C# с .Net 4.0 фреймворк. Это создано после обращения к Образцу Спецификации - Джеффом Перрином

в GetProducts() условия, которые будут использоваться, определены (жестко закодированы) внутри метода. Есть еще один метод с именем GetProductsBasedOnInputFilters(), В этом методе список спецификаций составляется как параметр метода.

ВОПРОС

Как лучше всего применять эти фильтры в списке продуктов, в этом методе?

Примечание: я пытался применить FindAll пункт внутри foreach цикл и добавление результатов в list, Но эта логика неверна - должны быть возвращены только те предметы, которые удовлетворяют всем условиям.

Примечание: количество спецификаций в списке productSpeifications будет зависеть от ввода пользователя

Примечание. Подход, упомянутый в разделе " Динамически построить фильтр LINQ для метода Any()? ", Представляется полезным. Однако я не уверен, как использовать этот подход здесь, так как я имею дело со списком specifications; не generic delegates,

Методы фильтра

public static class ProductFilterHelper
{
    public static List<Product> GetProducts(List<Product> list)
    {
        double priceLimit = 100;

        //FIRST::
        //List<Product> selectedList =  list.FindAll(new OnSaleSpecification().IsSatisfiedBy);

        //SECOND::
        //AndSpecification<Product> spec = new AndSpecification<Product>(new OnSaleSpecificationForProduct(), new PriceGreaterThanSpecificationForProduct(priceLimit));
        //List<Product> selectedList = list.FindAll(spec.IsSatisfiedBy); 


        //THIRD:
        List<Product> selectedList = list.FindAll(new OnSaleSpecificationForProduct()
                                                       .And(new PriceGreaterThanSpecificationForProduct(priceLimit))
                                                       .And(new PriceGreaterThan105())
                                                       .IsSatisfiedBy
                                                  );

        return selectedList;
    }

    public static List<Product> GetProductsBasedOnInputFilters(List<Product> productList, List<Specification<Product>> productSpeifications)
    {
        List<Product> selectedList = new List<Product>();

        foreach (Specification<Product> specification in productSpeifications)
        {
            List<Product> currentList = productList.FindAll(specification.IsSatisfiedBy);

            if (currentList != null && currentList.Count > 0)
            {
                foreach (Product p in currentList)
                {
                    if (!selectedList.Contains(p))
                    {
                        selectedList.Add(p);
                    }
                }
            }
        }

        return selectedList;
    }        

}

клиент

class Program
{

    static void Main(string[] args)
    {

        List<Product> list = new List<Product>();

        Product p1 = new Product(false, 99);
        Product p2 = new Product(true, 99);
        Product p3 = new Product(true, 101);
        Product p4 = new Product(true, 110);
        Product p5 = new Product(false, 110);

        list.Add(p1);
        list.Add(p2);
        list.Add(p3);
        list.Add(p4);
        list.Add(p5);

        double priceLimit = 100;

        List<Specification<Product>> specifications = new List<Specification<Product>>();
        specifications.Add(new OnSaleSpecificationForProduct());
        specifications.Add(new PriceGreaterThanSpecificationForProduct(priceLimit));
        specifications.Add(new PriceGreaterThan105());

        List<Product> selectedList = ProductFilterHelper.GetProductsBasedOnInputFilters(list, specifications);

        Console.ReadKey();
    }

}

Аннотация Технические характеристики

public abstract class Specification<T>
{
    public abstract bool IsSatisfiedBy(T obj);

    public AndSpecification<T> And(Specification<T> specification)
    {
        return new AndSpecification<T>(this, specification);
    }

    public OrSpecification<T> Or(Specification<T> specification)
    {
        return new OrSpecification<T>(this, specification);
    }

    public NotSpecification<T> Not(Specification<T> specification)
    {
        return new NotSpecification<T>(this, specification);
    }
}

public abstract class CompositeSpecification<T> : Specification<T>
{
    protected readonly Specification<T> _leftSide;
    protected readonly Specification<T> _rightSide;

    public CompositeSpecification(Specification<T> leftSide, Specification<T> rightSide)
    {
        _leftSide = leftSide;
        _rightSide = rightSide;
    }
}

Общие характеристики

public class AndSpecification<T> : CompositeSpecification<T>
{
    public AndSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {

    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) && _rightSide.IsSatisfiedBy(obj);
    }
}

public class OrSpecification<T> : CompositeSpecification<T>
{
    public OrSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {
    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) || _rightSide.IsSatisfiedBy(obj);
    }
}

public class NotSpecification<T> : CompositeSpecification<T>
{
    public NotSpecification(Specification<T> leftSide, Specification<T> rightSide)
        : base(leftSide, rightSide)
    {
    }

    public override bool IsSatisfiedBy(T obj)
    {
        return _leftSide.IsSatisfiedBy(obj) && !_rightSide.IsSatisfiedBy(obj);
    }
}

характеристики продукта

public class OnSaleSpecificationForProduct : Specification<Product>
{
    public override bool IsSatisfiedBy(Product product)
    {
        return product.IsOnSale;
    }
}

public class PriceGreaterThanSpecificationForProduct : Specification<Product>
{
    private readonly double _price;
    public PriceGreaterThanSpecificationForProduct(double price)
    {
        _price = price;
    }

    public override bool IsSatisfiedBy(Product product)
    {
        return product.Price > _price;
    }
}

public class PriceGreaterThan105 : Specification<Product>
{

    public override bool IsSatisfiedBy(Product product)
    {
        return product.Price > 105;
    }
}

сущность

public class Product
{
    private bool _isOnSale;
    private double _price = 0.0;

    public Product(bool isOnSale)
        : this(isOnSale, 0.0)
    {
        _isOnSale = isOnSale;
    }

    public Product(double price)
        : this(false, price)
    {
        _price = price;
    }

    public Product(bool isOnSale, double price)
    {
        _price = price;
        _isOnSale = isOnSale;
    }

    public bool IsOnSale
    {
        get { return _isOnSale; }
    }

    public double Price
    {
        get { return _price; }
    }
}

РЕКОМЕНДАЦИИ

  1. Шаблон спецификации - Джефф Перрен
  2. Свободные интерфейсы и метод цепочки в C#
  3. Избегайте просмотра списка несколько раз с помощью linq, с динамическими условиями (фильтр)
  4. Динамически построить фильтр LINQ для метода Any()?

2 ответа

Решение

Вы можете сделать одну из нескольких вещей:

  • Объедините фильтры, укладывая Where вызовы друг на друга, как в ответе @Lijo

  • Проверьте все характеристики каждого элемента:

    return productList
      .Where(p => specifications.All(ps => ps.IsSatisfiedBy(p))
      .ToList()
    
  • Создайте составную спецификацию "И", которая принимает несколько дочерних элементов вместо двух:

    public class AndSpecification<T> : ISpecification<T>
    {
        private ISpecification<T>[] _components;
    
        public AndSpecification(ISpecification<T>[] components) 
        {
          _components = components;
        }
    
        public bool IsSatisfiedBy(T item) 
        {
          return components.All(c => c.IsSatisfiedBy(item));
        }
      }
    

Тогда вы могли бы сделать:

var allFiltersSpecification = new AndSpecification(specifications)
return productList.Where(allFiltersSpecification.IsSatisfiedBy);

Следующий код работает... Предложения приветствуются.

 public static List<Product> GetProductsBasedOnInputFilters(List<Product> productList, List<Specification<Product>> productSpecifications)
 {
            IEnumerable<Product> selectedList = productList;
            foreach (Specification<Product> specification in productSpecifications)
            {
                selectedList = selectedList.Where(specification.IsSatisfiedBy);
            }
            return selectedList.ToList();
 }

Стоит взглянуть и на следующее.

  1. Основы дерева выражений
  2. Создание динамических методов с деревьями выражений в Visual Studio 2010
  3. Динамическое составление предикатов выражений
  4. Как динамически сочетать условия?
Другие вопросы по тегам