哪个设计模式用于过滤查询? C#

时间:2009-02-08 07:55:34

标签: c# asp.net design-patterns

我有一个包含产品列表(服装)的数据库表。这些产品属于不同的商店。

样品类别:上衣,下装,鞋

样品商店:gap.com,macys.com,target.com

我的客户可以通过以下方式请求过滤产品:

  • 所有产品(无过滤器)
  • 按类别
  • by store
  • 按类别和商店

现在我的“Products”类中有一个方法,它根据用户请求的过滤器类型返回产品。我使用FilterBy枚举来确定需要返回哪些产品。

例如,如果用户想要查看“tops”类别中的所有产品,我会调用此函数:

Products.GetProducts(FilterBy.Category, "tops", ""); 

我的最后一个参数是空的,因为它是包含要过滤的“store”的字符串,但在这种情况下没有商店。但是,如果用户想要按类别和商店进行过滤,我会以这种方式调用方法:

Product.GetProducts(FilterBy.CategoryAndStore, "tops", "macys.com");

我的问题是,有什么更好的方法呢?我刚学会了战略设计模式。我可以用它来更好地(更容易扩展和更容易维护)吗?

我问这个问题的原因是因为我认为这一定是人们反复解决的一个非常普遍的问题(以各种方式过滤产品)

6 个答案:

答案 0 :(得分:13)

根据Eric Evan的“域驱动设计”,您需要规范模式。像这样的东西

public interface ISpecification<T>
{
  bool Matches(T instance);
  string GetSql();
}

public class ProductCategoryNameSpecification : ISpecification<Product>
{
  readonly string CategoryName;
  public ProductCategoryNameSpecification(string categoryName)
  {
    CategoryName = categoryName;
  }

  public bool Matches(Product instance)
  {
    return instance.Category.Name == CategoryName;
  }

  public string GetSql()
  {
    return "CategoryName like '" + { escaped CategoryName } + "'";
  }
}

现在可以使用规范

调用您的存储库
var specifications = new List<ISpecification<Product>>();
specifications.Add(
 new ProductCategoryNameSpecification("Tops"));
specifications.Add(
 new ProductColorSpecification("Blue"));

var products = ProductRepository.GetBySpecifications(specifications);

您还可以创建一个通用的CompositeSpecification类,该类包含子规范和关于应用于哪个逻辑运算符的指示符和/或

我更倾向于结合LINQ表达式。

更新 - 运行时LINQ的示例

var product = Expression.Parameter(typeof(Product), "product");
var categoryNameExpression = Expression.Equal(
  Expression.Property(product, "CategoryName"),
  Expression.Constant("Tops"));

您可以像这样添加“和”

var colorExpression = Expression.Equal(
  Expression.Property(product, "Color"),
  Expression.Constant("Red"));
var andExpression = Expression.And(categoryNameExpression, colorExpression);

最后,您可以将此表达式转换为谓词,然后执行它...

var predicate = 
  (Func<Product, bool>)Expression.Lambda(andExpression, product).Compile();
var query = Enumerable.Where(YourDataContext.Products, predicate);

foreach(Product currentProduct in query)
  meh(currentProduct);

可能不会编译,因为我已将其直接输入浏览器,但我相信它通常是正确的。

另一次更新: - )

List<Product> products = new List<Product>();
products.Add(new Product { CategoryName = "Tops", Color = "Red" });
products.Add(new Product { CategoryName = "Tops", Color = "Gree" });
products.Add(new Product { CategoryName = "Trousers", Color = "Red" });
var query = (IEnumerable<Product>)products;
query = query.Where(p => p.CategoryName == "Tops");
query = query.Where(p => p.Color == "Red");
foreach (Product p in query)
    Console.WriteLine(p.CategoryName + " / " + p.Color);
Console.ReadLine();

在这种情况下,您将在内存中进行评估,因为源是List,但如果您的源是支持Linq2SQL的数据上下文,例如我想想,那么将使用SQL进行评估。

您仍然可以使用规范模式,以使您的概念明确。

public class Specification<T>
{
  IEnumerable<T> AppendToQuery(IEnumerable<T> query);
}

两种方法的主要区别在于后者基于显式属性构建已知查询,而第一种方法可用于构建任何结构的查询(例如,完全从XML构建查询)。

这应该足以让你入门: - )

答案 1 :(得分:2)

策略模式不一定能与基于通用接口的存储库方法完美结合。就个人而言,我可能会选择以下两种方式之一:

  • 一种支持选项组合的搜索方法:

    IList<Product> GetProducts(string category, string store, ...);

(然后有选择地应用过滤器的组合(即null表示“任何”) - 在构建命令时,或传递给执行类似操作的SPROC。

  • 使用LINQ,可能是一个谓词表达式?

    IList<Product> GetProducts(Expression<Func<Product,bool>> predicate);

当然,使用LINQ你也可以使用调用者的组合,但是编写一个封闭/经过全面测试的存储库更难:

 `IQueryable<Product> Products {get;}`

(并且让来电者使用。在哪里(x =&gt; x.Category ==“foo”)) - 我对这最后一个长期不太确定......

答案 2 :(得分:1)

我想我会创建一个Category类和一个Store类,而不仅仅是字符串:

class Category
{
  public Category(string s)
  {
    ...
  }
  ...
}

然后可能:

Product.GetProducts(
  Category category, //if this is null then don't filter on category
  Store store //if this is null then don't filter on store
  )
{
  ...
}

CategoryStore类可能相关(它们可能都是Filter类的子类)。

答案 3 :(得分:0)

我基于对模式的一点知识来回答这个问题。

装饰图案可能会有所帮助(考虑到你可以添加一个过滤器并获得结果。在其上应用新过滤器并获得新结果)

答案 4 :(得分:0)

我会选择类似过滤器本身的策略并编写 CategoryFilter StoreFilter 类。然后我会使用复合或装饰器来组合过滤器。

答案 5 :(得分:0)

你不能在这里添加Where的东西吗?

var products = datacontext.Products;

if(!String.IsNullOrEmpty(type))
  products = products.Where(p => p.Type == type);

if(!String.IsNullOrEmpty(store))
  products = products.Where(p => p.Store == store);

foreach(var p in products)
  // Do whatever

或类似的......