Согласно «Эскизу Эвану» «Domain Drive Design», вам нужен шаблон спецификации. Как то так
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();
В этом случае вы будете выполнять оценку в памяти, поскольку источником является список, но если вашим источником является контекст данных, который поддерживает Linq2SQL, например, я думаю, , это будет оцениваться с использованием SQL.
Вы все еще можете использовать шаблон Спецификации, чтобы сделать ваши концепции явными.
public class Specification<T>
{
IEnumerable<T> AppendToQuery(IEnumerable<T> query);
}
Основное различие между этими двумя подходами заключается в том, что последний создает известный запрос на основе явных свойств, тогда как первый может использоваться для создания запроса любой структуры (например, для создания запроса исключительно из XML).
Этого должно быть достаточно, чтобы начать: -)