Усунення дублювання Where Expressions в додатку

Припустимо, у вас є товари і категорії. У якийсь момент клієнт повідомляє, що для категорій з рейтингом > 50 необхідно використовувати інші бізнес-процеси. У вас достатньо досвіду, і ви розумієте, що де сьогодні 50 завтра буде 127.37 і хочете уникнути появи магічних чисел в коді, тому робите так:

public class Category : HasIdBase<int>
{
public static readonly Expression<Func<Category, bool>> NiceRating = x => x.Rating > 50;

//...
}

var niceCategories = db.Query<Category>.Where(Category.NiceRating);

На жаль, цей номер не пройде, якщо ви хочете вибрати продукти з відповідних категорій:

public class Product: HasIdBase<int>
{
public virtual Category Category { get; set; }

//...
}

var niceProductsCompilationError = db.Query<Product>.Where(Category.NiceRating); // так можна!

На щастя, усунути цей недолік досить просто!

// Фактично ми реалізуємо композицію виразів,
// яка дасть нам вираз, відповідне композиції цільових функцій
public static Expression<Func<TIn, TOut>> Compose<TIn, TInOut, TOut>(
this Expression<Func<TIn, TInOut>> input
, Expression<Func<TInOut, TOut>> inOutOut
, TIn inParam = default(TIn))
{
// це параметр x => blah-blah. Для LINQ нам потрібен null
var param = Expression.Parameter(typeof(TIn), inParam);
// отримуємо об'єкт, до якого застосовується вираз
var invoke = Expression.Invoke(input, param);
// і виконуємо "отримай об'єкт і застосуй до нього його вираз"
var res = Expression.Invoke(inOutOut, invoke);

// повертаємо лямбду потрібного типу
return Expression.Lambda<Func<TIn, TOut>>(res, param);
}

// Додаємо "просунутий" варіант Where
public static IQueryable < T> Where<T, TParam>(this IQueryable < T> queryable
, Expression<Func<T, TParam>> prop
, Expression<Func<TParam, bool>> where)
{
return queryable.Where(prop.Compose(where));
}

// Перевіряємо
[Fact]
public void AdvancedWhere_Works()
{
var product = new Product(new Category() {Rating = 700}, "Some Product", 100500);
var q = new[] {product}.AsQueryable();

var values = q.Where(x => x.Category Category.NiceRating).ToArray();
Assert.Equal(700, values[0].Category.Rating);
}

Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.