Pull to refresh
198
0
Максим Аршинов @marshinov

В саббатикале

Send message

Кроме этого C#-код лежит себе спокойненько в репозитории. Для него божественный тулинг, его можно разложить по папкам, а хранимок будет простыня в БД:)

А потом у вас хранимые процедуры на 1000 строк, которые никто не хочет исправлять, потому что боится что-то сломать. А еще нужно эти процедуры проверять, когда меняется структура БД. В друг какие-то колонки входят в процедуры. LINQ упадет на этапе компиляции. Так что смысл, безусловно, есть.

Сделать хороший доклад: сформулировать проблему, показать примеры сценариев, где linq2db справляется лучше, рассказать о деталях реализации, о технических вызовах, которые пришлось преодолеть при разработке, вставить пару шуточек или мемасиков (со вкусом, не перебарщивая). Навскидку, позиционирование может быть таким: ”linq2db — гибкий как Dapper, но без россыпи SQL в C# коде. Make LINQ great again”:)

А не хотите на DotNext сделать доклад про linq2db?

Я вот об этом


// GET: api/Todo
[EnableQuery()]  // requires using Microsoft.AspNet.OData;
[HttpGet]
public ActionResult<IQueryable<TodoItem>> GetTodoItems()
{
    return _context.TodoItems;
}

Да, это действительно удобно. С другой стороны и на стороне бекенда это не так сложно сделать на всяких автомаперах/мапстерах. Я думаю, что вопрос больше в том, на сколько разработчики клиентов знакомы с GraphQL и хотят с ним работать.

(*может течь, если уровень вложенности select'a становится больше какого-то, задаваемого разработчиком, уровня)

Я имел в виду именно такие варианты. Речь не о IQueryable как таковом, а о том, чтобы выдавать наружу язык запросов. Наверняка существуют API, для которых Graphql — самое то. Но для многих других rest-like вполне достаточно.

И зачем коды ответа в HTTP придумали...

Где в IQueryable сказано о базе и о времени жизни? Хм, может быть в IQueryProvider? Нет? Что же это — «детали реализации» получается? Может быть, даже существуют провайдеры, работающие со внешним API, а не БД в качестве источника данных? Так это, выходит, что разные реализации провайдеров не взаимозаменяемы? Как же назывался этот принцип?

Не совсем понятно, зачем нужен DropdownOption? Кажется, что если все данные отдаются на фронт, то T не используется, и можно обойтись DropdownOption.

Если LINQ вида q.Where(/**/).ToDropdownOption(/**/) нельзя использовать, потому что не транслируется или слишком тормозит, есть вариант получить данные без LINQ. Тогда сигнатура метода будет что-то вроде Task<IEnumerable<DropdownOption>>. При этом тип T мы скорее всего знаем. Поэтому базовый класс без T используется только для полиморфизма, а в методе получения данных используется типобезопасная версия.


А как обстоят дела с перфомансом? Где границы применимости данного решения, при переходе которых база будет загружена (количество пользователей, одновременно запрашивающих фильтры/ количество колонок в таблице / количество записей в таблице)?

Границы определяет стресс-тестирование на целевом железе. Здесь скорее вопрос usability. Если в каждом фильтре по 1000 значений, то гнать по сети дропдауны не лучшая затея. В этом случае нужно добавлять автокомплиты.


В примере GetDropdownOptionsAsync возвращает все доступные фильтры для таблицы.
Насколько усложнится код, если нужны разные наборы фильтров для одной таблицы? Например, из-за разных прав доступа.

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

Так и вправду лучше. Я скорее имел в виду, что проблема "страшного ужасного форматирования, лишних пробелов и табов" кажется надуманной в мире, где IDE в подсказказ разве что еще со стек оверфлоу код не предлагают тебе скопипастить (это, я думаю, не за горами уже:)

Ну если все сервисы унаследовать от одного базового в котором все в try/catch завернуть, то не так уж и не элегантно

Настройте форматирование в ide и заведите привычку нажимать на хоткей перел коммитом. Пять минут времени и никаких пробелов

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;
    }
}

Как-то так, видимо, будет

Вы проигнорировали все пункты моего предыдущего комментария, начиная с пятого. Обозначаю проблемы, указанные в пятом пункте еще раз в явном виде: без дополнительных абстракций будет много повторов и неконсистентное api. IDropdownProvider<T> — это и есть "обертка" в вашей терминологии. Отдельный обобщенный интерфейс лучше, чем горсть репозиториев/сервисов, потому что один интерфейс лучше, чем пачка.

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


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

Перейдите по ссылочке из сноски в статье. Там тема логично продолжается в контексте C#, без C++.


TLDR


public static class Singleton
{
    public static Singleton Instance { get; } = new Singleton();
    private Singleton()
   {
        //...
   }
}

Хотя, очень вероятно, что вы пользуетесь IOC/DI. И тогда все еще проще.

Ну не знаю. Epic / Feature / Task довольно распространённая иерархия. Че-то никто из знакомых проблем с ней не испытывает. То что у вас в начале нарисовано — это горе от ума какое-то

Information

Rating
Does not participate
Location
Казань, Татарстан, Россия
Works in
Date of birth
Registered
Activity