Устранение дублирования Where Expressions в приложении

в 16:52, , рубрики: .net, C#, expression trees

Допустим, у вас есть товары и категории. В какой-то момент клиент сообщает, что для категорий с рейтингом > 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);
	}

Спасибо за внимание!

Автор: marshinov

Источник

Поделиться новостью

* - обязательные к заполнению поля