- PVSM.RU - https://www.pvsm.ru -
Как гласит Википедия:
«Спецификация» в программировании — это шаблон проектирования [1], посредством которого представление правил бизнес логики может быть преобразовано в виде цепочки объектов, связанных операциями булевой логики [2].
Реализация и преимущества данного шаблона уже были описаны в нескольких [3]статьях [4], но т.к. у меня в проекте уже была своя реализация, которая, на мой взгляд, удобнее и позволяет убрать кучу повторяющегося кода, то я решил поделиться своим вариантом (который, возможно, не совсем чистая Спецификация).
Исходники традиционно на GitHub [5], пакеты на Nuget [6].
Теперь к деталям: данная либа будет полезна, в первую очередь, для тех, у кого есть большое количество бизнес-логики при фильтрации или множество параметров фильтрации. Как пример, бэкенд для грида типа такого https://reactdatagrid.io/demo [7] или фильтра типа такого https://i.imgur.com/Jw5UAFz.png [8].
Итак, как может выглядеть типичный код для получения данных в апи:
// Модель данных
public class Employee
{
public decimal Salary { get; set; }
public string Name { get; set; }
public DateTime? Date { get; set; }
}
// Фильтр на фронте
public class SomeApiFilter
{
public DateTime? Date { get;set }
public string Name { get;set }
public string NameContains { get;set }
public decimal? SalaryFrom { get; set; }
public decimal? SalaryTo { get; set; }
// И еще 100500 полей, которые хочет заказчик
}
// Пришел в запросе
var filter = new SomeApiFilter
{
Date = DateTime.Today,
NameContains = "complex",
IdFrom = 0,
IdTo = 5
};
// В коде репозитория (или контроллера -_o)
var where = PredicateBuilder.New<Employee>();
if (filter.Date.HasValue)
{
where.And(f => f.Date == filter.Date.Value);
}
if (!string.IsNullOrEmpty(filter.Name))
{
where.And(f => f.Name == filter.Name);
}
if (!string.IsNullOrEmpty(filter.NameContains))
{
where.And(f => f.Name.Contains(filter.NameContains));
}
if (filter.SalaryFrom.HasValue)
{
where.And(f => f.Id >= filter.SalaryFrom);
}
if (filter.SalaryTo.HasValue)
{
where.And(f => f.Id <= filter.SalaryTo);
}
// И еще 100500 if
//Получаем данные
var data = dbcontext.Set<Employee>().Where(where);
Как может помочь моя библиотека, при условии соблюдения конвенций наименования и типов полей фильтра:
// где-то в DAL создаем обработчик фильтра
public class GetByFilterSpec : SpecificationBase<Employee, SomeApiFilter>
{
public GetByFilterSpec(ILogger<GetByFilterSpec> logger, IOptions<Options> options)
: base(logger, options)
{
// можно добавить явную обработку полей фильтра, но по умолчанию не надо
}
}
// Подключаем библиотеку
services.AddLinqSpecification();
//Регистрируем спецификацию
services.AddSingleton<GetByFilterSpec>();
// модифицируем фильтр, используя новые типы свойств
public class SomeApiFilter
{
public RangeFilter<decimal> Salary { get; set; }
public StringFilter Name { get; set; }
public DateTime? Date { get;set }
// И еще 100500 полей, которые хочет заказчик
}
//Используем
// Пришел с фронта
var filter = new SomeApiFilter
{
Date = DateTime.Today,
Salary = new RangeFilter<decimal> { Start = 0, End = 5 },
Name = new StringFilter("complex") { Contains = true }
}
// В коде репозитория (или контроллера -_o)
var spec = serviceProvider.GetRequiredService<GetByFilterSpec>();
var expression = spec.CreateFilterExpression(filter);
var data = dbcontext.Set<Employee>().Where(expression);
Таким образом, в оптимистичном варианте и для больших фильтров, количество кода может уменьшиться в десятки раз, буквально до 2 строк:
var expression = spec.CreateFilterExpression(filter);
var data = dbcontext.Set<Employee>().Where(expression);
Чуть больше примеров и вариантов использования описано в ридми к проекту и в тестах.
Буду рад выслушать конструктивную критику и предложения по вариантам реализации TODO из README :)
Автор: Вилен Т
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/374642
Ссылки в тексте:
[1] шаблон проектирования: https://ru.wikipedia.org/wiki/%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F
[2] булевой логики: https://ru.wikipedia.org/wiki/%D0%91%D1%83%D0%BB%D0%B5%D0%B2%D0%B0_%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D0%B0
[3] нескольких : https://habr.com/ru/post/325280/
[4] статьях: https://habr.com/ru/post/171559/
[5] GitHub: https://github.com/xumix/XSpecification
[6] Nuget: https://www.nuget.org/packages/XSpecification.Linq
[7] https://reactdatagrid.io/demo: https://reactdatagrid.io/demo
[8] https://i.imgur.com/Jw5UAFz.png: https://i.imgur.com/Jw5UAFz.png
[9] Источник: https://habr.com/ru/post/663808/?utm_source=habrahabr&utm_medium=rss&utm_campaign=663808
Нажмите здесь для печати.