Открыть список
Как стать автором
Обновить

Комментарии 9

ух ты, интересная и очень долгая имплементация «SELECT DISTINCT columname» используя новейшие фичи из C# 8.0

++ непонятно в чем, собственно, проблема и зачем так усложнять всё

Давайте посмотрим как сделать проще. Представьте, что фильтров таких у вас в системе сотни, а программистов работают десятки.


  • Класс 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 возвращает все доступные фильтры для таблицы.
Насколько усложнится код, если нужны разные наборы фильтров для одной таблицы? Например, из-за разных прав доступа.

Можно разграничить права вот так. В этом случае в код статьи не нужно будет вообще вносить изменений.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.