Русский
Русский
English
Статистика
Реклама

Cоздание переиспользуемых Linq фильтров (построителей предикатов для Where), которые можно применять для разных типов

Способ создания переиспользуемых Linq фильтров (построителей предикатов для условия Where), которые можно применять для разных типов объектов. Поля объектов для фильтрации указываются с помощью MemberExpression.

Способ подходит для Entity Framework, включая Async операции.

Основная идея. Что такое переиспользуемый фильтр?

Например есть приказы:

class Order { public DateTime Start { get; set; }public DateTime? End { get; set; }}

Пусть нужно найти все приказы которые будут действовать в ближайшие 7 дней.

С помощью переиспользуемого построителя фильтра (если бы он был реализован) найти приказы можно так:

var ordersFiltred = orders.WhereOverlap(// с помощью MemberExpressions// указываем по каким полям производить поискfromField: oo => oo.Start,toField: oo => oo.End,// указываем период поискаfrom: DateTime.Now,to: DateTime.Now.AddDays(7)).ToList();

Этот же WhereOverlap можно переиспользовать и применить к другому типу. Например, для поиска командировок:

class Trip { public DateTime? From { get; set; }public DateTime? To { get; set; }}
var tripsFiltred = trips.WhereOverlap(// с помощью MemberExpressions// указываем по каким полям производить поискfromField: oo => oo.From,toField: oo => oo.To,from: DateTime.Now,to: DateTime.Now.AddDays(7)).ToList();

Приказы и командировки - это разные типы объектов, у них нет общего интерфейса, поля для поиска называются по-разному. И все таки для обоих типов (и приказов и командировок) применяется один переиспользуемый фильтр WhereOverlap.

Ниже описано как можно делать такие переиспользуемые построители предикатов.

Как сделать переиспользуемый фильтр

Выше было описано применение WhereOverlap, было бы логично показать его реализацию. Но для того, чтобы сделать WhereOverlap, нужна реализация операторов И, ИЛИ. Поэтому начнем с более простого примера.

Пусть есть выплаты и премии:

class Payout { public decimal Total { get; set; }public bool UnderControl { get; set; }}class Premium {public decimal Sum { get; set; }public bool RequiresConfirmation { get; set; }}

Сделаем переиспользуемый фильтр для поиска платежей больше определенной суммы:

class UnderControlPayFilter {readonly decimal Limit;public UnderControlPayFilter(decimal limit) {Limit = limit;}public Expression<Func<TEnt, bool>> Create<TEnt>(Expression<Func<TEnt, decimal>> sumField) {// GreaterOrEqual - нужно реализовать// GreaterOrEqual - это extension, который принимает//  - указание на поле (Expression sumField)//  - и значение с которым нужно сравнивать (Limit)return sumField.GreaterOrEqual(Limit);}}

Пример использования UnderControlPayFilter фильтра:

// фильтр поиска платежей требующих дополнительного контроля//// конкретный предел (здесь 1000) можно вынести в настройки,// а UnderControlPayFilter зарегистрировать в IoC-контейнере.// Тогда можно централизовано (через найстройки приложения)// управлять максимальным пределомvar underControlPayFilter = new UnderControlPayFilter(1000);//// Применение переиспользуемого фильтра для выплатvar payoutPredicate =underControlPayFilter.Create<Payout>(pp => pp.Total);// здесь, для упрощения, payouts - это массив,// в реальном приложении это может быть Entity Framework DbSet var payouts = new[] {new Payout{ Total = 100 },new Payout{ Total = 50, UnderControl = true },new Payout{ Total = 25.5m },new Payout{ Total = 1050.67m }}.AsQueryable().Where(payoutPredicate).ToList();//// Применение переиспользуемого фильтра для премийvar premiumPredicate =underControlPayFilter.Create<Premium>(pp => pp.Sum);// здесь, для упрощения, premiums - это массив,// в реальном приложении это может быть Entity Framework DbSet var premiums = new[] {new Premium{ Sum = 2000 },new Premium{ Sum = 50.08m },new Premium{ Sum = 25.5m, RequiresConfirmation = true },new Premium{ Sum = 1070.07m }}.AsQueryable().Where(premiumPredicate).ToList();

Все готово, осталось только реализовать GreaterOrEqual extension:

public static class MemberExpressionExtensions {    public static Expression<Func<TEnt, bool>> GreaterOrEqual<TEnt, TProp>(        this Expression<Func<TEnt, TProp>> field, TProp val)            => Expression.Lambda<Func<TEnt, bool>>(                Expression.GreaterThanOrEqual(field.Body, Expression.Constant(val, typeof(TProp))),                 field.Parameters);}

По аналогии можно реализовать extension-ы LessOrEqual, Equal, HasNoVal и другие.

Более сложные переиспользуемые фильтры с операторами И и ИЛИ

Пускай в выборку должны попадать не только платежи больше определенного предела, но и те, которые специально отмечены, как требующие дополнительного контроля.

Дополним UnderControlPayFilter:

class UnderControlPayFilter {readonly decimal Limit;public UnderControlPayFilter(decimal limit) {Limit = limit;}public Expression<Func<TEnt, bool>> Create<TEnt>(Expression<Func<TEnt, decimal>> sumField,Expression<Func<TEnt, bool>> controlMarkField) {// PredicateBuilder нужно реализовать (см. ниже)return PredicateBuilder.Or(sumField.GreaterOrEqual(Limit),controlMarkField.Equal(true));}}

Пример использования:

// для выплатvar payoutPredicate =underControlPayFilter.Create<Payout>(sumField: pp => pp.Total,controlMarkField: pp => pp.UnderControl);// для премийvar premiumPredicate = underControlPayFilter.Create<Premium>(sumField: pp => pp.Sum,controlMarkField: pp => pp.RequiresConfirmation);

PredicateBuilder это A universal PredicateBuilder сделанный Pete Montgomery.

Заключение

Чтобы делать свои переиспользуемые фильтры, нужен только PredicateBuilder и MemberExpressionExtensions. Просто скопируйте их в свой проект. Переиспользуемые фильтры можно оформить как extension (как WhereOverlap), как статический хелпер или класс (как UnderControlPayFilter).

Я сделал парочку переиспользуемых фильтров - GitHub, NuGet (включает PredicateBuilder и MemberExpressionExtensions).

Источник: habr.com
К списку статей
Опубликовано: 15.04.2021 22:17:49
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Программирование

Net

C

Entityframework

Entity

Entity framework

Expression

Expression trees

Expression tree

Predicate

Linq

Entity framework core

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru