2014-01-27 3 views
0

У меня есть код C# с .Net 4.0 рамки. Это создается после ссылки The Specification Pattern - by Jeff PerrinКак применить несколько условий фильтра (одновременно) в списке?

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

ВОПРОС

Что такое лучший способ применить эти фильтры в списке продуктов, в этом методе?

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

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

Примечание: Подход упоминается в «Dynamically build LINQ filter for the Any() method?» кажется полезным. Однако я не уверен, как использовать этот подход здесь, так как я имею дело со списком 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; 
    }   

} 

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

} 

Аннотация Характеристики

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

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

ЛИТЕРАТУРЫ

  1. The Specification Pattern - by Jeff Perrin
  2. Fluent interfaces and Method Chaining in C#
  3. Avoid to browse a list multiple time with linq, with dynamic conditions (filter)
  4. Dynamically build LINQ filter for the Any() method?
+1

Обязательные элементы в списке соответствуют * всем * спецификациям или * любым * из них? Также почему бы просто не сделать что-то [подобное http://www.stackoverflow.com/a/16227811/119477) –

+0

@ConradFrix Он должен соответствовать всем спецификациям. Подход, упомянутый в [Динамически создайте LINQ-фильтр для метода Any()?] (Http://stackoverflow.com/questions/16227743/dynamically-build-linq-filter-for-the-any-method), кажется полезным. Однако я не уверен, как использовать это здесь, так как я имею дело со списком «спецификаций»; а не «общие делегаты». – Lijo

+0

UPDATE: Related Question: https://stackoverflow.com/questions/21406492/how-to-combine-conditions-dynamically – Lijo

ответ

1

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

  • Объединить Filte RS путем укладки 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); 
+0

Спасибо .. Я планирую пойти с 'All' .. Мой текущий код -' return productList. Где: (p => productSpecifications.All (ps => ps.IsSatisfiedBy (p))). ToList(); ' – Lijo

+0

Ссылка: - [Запрос LINQ с использованием ALL] (http://stackoverflow.com/questions/7285416/linq -query-using-all) – Lijo

+0

Я разместил связанный с этим вопрос http://stackoverflow.com/questions/21406492/how-to-combine-conditions-dynamically – Lijo

1

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

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. Expression Tree Basics
  2. Generating Dynamic Methods with Expression Trees in Visual Studio 2010
  3. Dynamically Composing Expression Predicates
  4. How to combine conditions dynamically?

 Смежные вопросы

  • Нет связанных вопросов^_^