根据 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 类,该类将包含子规范和一个关于将哪个逻辑运算符应用于它们的指示符 AND/OR
不过,我更倾向于组合 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();
在这种情况下,您将在内存中进行评估,因为源是一个列表,但如果您的源是支持 Linq2SQL 的数据上下文,例如我认为这将使用 SQL 进行评估。
您仍然可以使用规范模式来明确您的概念。
public class Specification<T>
{
IEnumerable<T> AppendToQuery(IEnumerable<T> query);
}
这两种方法的主要区别在于,后者基于显式属性构建已知查询,而第一种方法可用于构建任何结构的查询(例如完全从 XML 构建查询。)
这应该足以让你开始:-)