У меня есть код 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; }
}
}
ЛИТЕРАТУРЫ
- The Specification Pattern - by Jeff Perrin
- Fluent interfaces and Method Chaining in C#
- Avoid to browse a list multiple time with linq, with dynamic conditions (filter)
- Dynamically build LINQ filter for the Any() method?
Обязательные элементы в списке соответствуют * всем * спецификациям или * любым * из них? Также почему бы просто не сделать что-то [подобное http://www.stackoverflow.com/a/16227811/119477) –
@ConradFrix Он должен соответствовать всем спецификациям. Подход, упомянутый в [Динамически создайте LINQ-фильтр для метода Any()?] (Http://stackoverflow.com/questions/16227743/dynamically-build-linq-filter-for-the-any-method), кажется полезным. Однако я не уверен, как использовать это здесь, так как я имею дело со списком «спецификаций»; а не «общие делегаты». – Lijo
UPDATE: Related Question: https://stackoverflow.com/questions/21406492/how-to-combine-conditions-dynamically – Lijo