简体   繁体   English

如何在列表上同时应用多个过滤条件?

[英]How to apply multiple filter conditions (simultaneously) on a list?

I have following C# code with .Net 4.0 framework. 我在.Net 4.0框架中使用以下C#代码。 This is created after referring The Specification Pattern - by Jeff Perrin 这是在参考“规范模式”之后创建的-Jeff Perrin

In the GetProducts() the conditions to be used are defined (hard coded) inside the method. GetProducts()中,要使用的条件在方法内部定义(硬编码)。 There is another method named GetProductsBasedOnInputFilters() . 还有另一个名为GetProductsBasedOnInputFilters()方法。 In this method the list of specifications are made as parameter to the method. 在此方法中,规格列表作为方法的参数。

QUESTION

What is the best way to apply these filters on the list of products, in this method? 用这种方法将这些过滤器应用于产品列表的最佳方法是什么?

Note : I have tried applying the FindAll clause inside a foreach loop and adding the results in a list . 注意 :我尝试在foreach循环中应用FindAll子句,并将结果添加到list But that logic is incorrect - only those items that satisfy all of the conditions need to be returned. 但是这种逻辑是不正确的-只有那些满足所有条件的项目才需要返回。

Note : The number of specifications in the productSpeifications list will vary based on user input 注意 :productSpeifications列表中的规格数将根据用户输入而有所不同

Note : The Approach mentioned in " Dynamically build LINQ filter for the Any() method? " seems useful. 注意 :“ 为Any()方法动态构建LINQ筛选器? ”中提到的方法似乎很有用。 However I am not sure how to use this approach here since I am dealing with List of specifications ; 但是由于我正在处理specifications列表,所以我不确定在这里如何使用这种方法。 not generic delegates . 不是generic delegates

Filter Methods 筛选方法

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;
    }        

}

Client 客户

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();
    }

}

Abstract Specifications 摘要规格

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;
    }
}

Generic Specifications 通用规格

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);
    }
}

Product Specifications 产品规格

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;
    }
}

Entity 实体

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; }
    }
}

REFERENCES 参考

  1. The Specification Pattern - by Jeff Perrin 规范模式-Jeff Perrin
  2. Fluent interfaces and Method Chaining in C# C#中的流畅接口和方法链接
  3. Avoid to browse a list multiple time with linq, with dynamic conditions (filter) 避免在动态条件下使用linq多次浏览列表(过滤器)
  4. Dynamically build LINQ filter for the Any() method? 为Any()方法动态构建LINQ筛选器?

You can do one of several things: 您可以执行以下操作之一:

  • Combine the filters by stacking Where invocations on top of each other, like in @Lijo's answer 通过相互堆叠Where调用来组合过滤器,如@Lijo的答案

  • Check all specifications on each item: 检查每个项目的所有规格:

     return productList .Where(p => specifications.All(ps => ps.IsSatisfiedBy(p)) .ToList() 
  • Create a composite 'And' specification that accepts multiple children instead of just two: 创建一个复合的“ And”规范,该规范接受多个子代,而不是两个子代:

     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)); } } 

Then you could do: 然后,您可以执行以下操作:

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

Following code works... Suggestions are welcome. 以下代码有效...欢迎提出建议。

 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();
 }

It is worth to take a look at the following too.. 也值得一看以下内容。

  1. Expression Tree Basics 表达式树基础
  2. Generating Dynamic Methods with Expression Trees in Visual Studio 2010 在Visual Studio 2010中使用表达式树生成动态方法
  3. Dynamically Composing Expression Predicates 动态组成表达式谓词
  4. How to combine conditions dynamically? 如何动态组合条件?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM