Comments 9
++ непонятно в чем, собственно, проблема и зачем так усложнять всё
Давайте посмотрим как сделать проще. Представьте, что фильтров таких у вас в системе сотни, а программистов работают десятки.
- Класс
DropdownOption
понадобится в любом случае для сериализации - Инициализаторы полей нужны, потому что в случае использования конструктора в проекции
.Select(x => new DropdownOption(/*...*/))
не будет работать сортировка.OrderBy(/*...*/)
. - Оставить публичные
{ get; set; }
поля можно, но для таких конструкцийq.ToList().Select(x => new DropdownOption(/*...*/) {/*...*/})
не получится гарантировать инициализацию полейLabel
иValue
. О проблемах частичной инициализации вы можете прочитать здесь - Использование
.ToDropDownOption
в связке с типобезопасным конструктором позволяет гарантировать верную инициализацию объекта в обоих сценариях:IQueryable
иIEnumerable
соответственно - «SELECT DISTINCT columname» где-то нужно написать и передать на фронтенд. Каждый разработчик будет писать метод самостоятельно? Не DRY: удачи с поддержкой консистентного API.
- Если в таблице 10-20 колонок и по каждому нужен фильтр, то параллельное выполнение уже не кажется такой плохой идеей
- Чтобы выполнить запросы параллельно потребуется по экземпляру
DbContext
/ISession
/Другая абстракция для работы с бд - Строить параллельные запросы самому каждый раз хлопотно, вот вам строитель. Предложите вариант проще, я его с радостью буду использовать.
Обозначьте проблему, почему обычный
DbContext
.Items
.Select(item => item.Date)
.Distinct()
.OrderBy(date => date)
.Select(date=> new DropDownOption(date, date.ToString('d')))
.ToList()
Обёрнутый в метод сервиса\репозитория\ещё кого то не решает задачу?
Вы проигнорировали все пункты моего предыдущего комментария, начиная с пятого. Обозначаю проблемы, указанные в пятом пункте еще раз в явном виде: без дополнительных абстракций будет много повторов и неконсистентное api. IDropdownProvider<T>
— это и есть "обертка" в вашей терминологии. Отдельный обобщенный интерфейс лучше, чем горсть репозиториев/сервисов, потому что один интерфейс лучше, чем пачка.
Будет здорово когда/если ковариантные возвращаемые типы будут реализованы. С ними можно избавиться от перекрытия через new. Пока имеем, что имеем.
Способ на самом деле удобный. Кажется, что ковариантные возвращаемые значения все-равно не спасут, потому что у свойства еще сеттер есть. Его вроде можно сделать private/protected, если кроме EF Core никто не будет его использовать.
public interface IDropdownOption
{
public object Value { get; }
public string Label { get; }
}
public class DropdownOption<T>: IDropdownOption
{
public TValue Value { get; internal init; }
public string Label { get; internal init; }
public DropdownOption(T value, string label)
{
Label = label;
Value = value;
}
}
Как-то так, видимо, будет
Не совсем понятно, зачем нужен generic DropdownOption? Кажется, что если все данные отдаются на фронт, то T не используется, и можно обойтись DropdownOption.
А как обстоят дела с перфомансом? Где границы применимости данного решения, при переходе которых база будет загружена (количество пользователей, одновременно запрашивающих фильтры/ количество колонок в таблице / количество записей в таблице)?
В примере GetDropdownOptionsAsync возвращает все доступные фильтры для таблицы.
Насколько усложнится код, если нужны разные наборы фильтров для одной таблицы? Например, из-за разных прав доступа.
Не совсем понятно, зачем нужен DropdownOption? Кажется, что если все данные отдаются на фронт, то T не используется, и можно обойтись DropdownOption.
Если LINQ вида q.Where(/**/).ToDropdownOption(/**/)
нельзя использовать, потому что не транслируется или слишком тормозит, есть вариант получить данные без LINQ. Тогда сигнатура метода будет что-то вроде Task<IEnumerable<DropdownOption>>
. При этом тип T мы скорее всего знаем. Поэтому базовый класс без T используется только для полиморфизма, а в методе получения данных используется типобезопасная версия.
А как обстоят дела с перфомансом? Где границы применимости данного решения, при переходе которых база будет загружена (количество пользователей, одновременно запрашивающих фильтры/ количество колонок в таблице / количество записей в таблице)?
Границы определяет стресс-тестирование на целевом железе. Здесь скорее вопрос usability. Если в каждом фильтре по 1000 значений, то гнать по сети дропдауны не лучшая затея. В этом случае нужно добавлять автокомплиты.
В примере GetDropdownOptionsAsync возвращает все доступные фильтры для таблицы.
Насколько усложнится код, если нужны разные наборы фильтров для одной таблицы? Например, из-за разных прав доступа.
Можно разграничить права вот так. В этом случае в код статьи не нужно будет вообще вносить изменений.
Делаем фильтры «как в экселе» на ASP.NET Core