【问题标题】:How to combine conditions dynamically?如何动态组合条件?
【发布时间】:2014-01-28 13:12:37
【问题描述】:

这个问题是对已经回答的问题How to apply multiple filter conditions (simultaneously) on a list?的增强

在上述问题中,我们有一个在所有规范上应用AND 运算符的方法。这是通过在规范上使用 LINQ All 运算符来实现的。

    public static List<Product> GetProductsUisngAndFilters(List<Product> productList, List<Specification<Product>> productSpecifications)
    {
        return productList.Where(p => productSpecifications.All(ps => ps.IsSatisfiedBy(p))).ToList();
    }

我们需要创建一个新方法 (GetProductsUisngDynamicFilters),它能够执行 ANDORNOT 规范(以及它们的混合)。知道我们如何解决这个问题吗?

过滤方法

public static class ProductFilterHelper
{
     public static List<Product> GetProductsUisngAndFilters(List<Product> productList, List<Specification<Product>> productSpecifications)
    {
        return productList.Where(p => productSpecifications.All(ps => ps.IsSatisfiedBy(p))).ToList();
    }
}

客户

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

【问题讨论】:

    标签: c# linq design-patterns fluent-interface specification-pattern


    【解决方案1】:

    通过查看您提供的代码,在我看来您组合过滤器的逻辑是合理的。问题是List&lt;Specification&lt;T&gt;&gt;。通过制定复合规范,您可以组合它们并且只传递一个Specification&lt;T&gt;(这将是一个CompositeSpecification&lt;T&gt;):

    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;
    
             var specification =
                 new OnSaleSpecificationForProduct()
                     .And(new PriceGreaterThanSpecificationForProduct(priceLimit)
                                  .Or(new PriceGreaterThan105()));
    
            List<Product> selectedList = ProductFilterHelper.GetProductsBasedOnInputFilters(list, specification);
    
            Console.ReadKey();
        }
    
    }
    

    而你的过滤方式变成:

     public static List<Product> GetProductsUisngDynamicFilters(List<Product> productList, Specification<Product> productSpecification)
        {
            return productList.Where(p => productSpecification.IsSatisfiedBy(p))
                              .ToList();
        }
    

    作为旁注,您应该考虑将OrAndNot 方法从抽象Specification&lt;T&gt; 移动到扩展方法。也许使用接口代替。

    【讨论】:

      猜你喜欢
      • 2020-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-10
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 2016-12-21
      相关资源
      最近更新 更多