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

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

В процессе работы я начал использовать Moq и долго страдал

Попробуйте FakeItEasy. Я долгое время использовал Moq, но попробовал его и мне он очень понравился - с тех пор все тесты только на нем. Код с ним намного лаконичнее.

Поставил вам в проект звезду за ваши старания, но, если честно, вот не знаю - есть ли какой-то резон использовать вашу библиотеку вместо веками проверенных решений, например, с Quartz.Net, или Hangfire, или даже любого Message Broker (например, в случае, если он и так уже в проекте используется для чего-либо).

есть ли какой-то резон использовать вашу библиотеку вместо веками проверенных решений, например, с Quartz.Net, или Hangfire

Нет никакого - она для этого даже не предназначена. Она - не планировщик.

PS А к Moq у меня претензия была, в основном, к документации. Но сейчас я уже претерпелся.

Я имел в виду другое. И Quartz.NET, и Hangfire очень часто используют именно не как планировщики, а как менеджеры фоновых задач. Потому что любую фоновую задачу можно легко рассматривать как задачу планировщика, только не поставленную на какой-либо расписание, а запущенную сразу. И API и того и другого напрямую это поддерживает (запуск любой задачи сразу без расписания).

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

А насчет использовать... Я отлично понимаю, что эта библиотека как решение - весьма нишевое, нужное реально немногим: индустрия сейчас пошла по другому пути. Но меня это не смущает - писал я ее чисто для своих целей, можно сказать - для своего удовольствия.

PS За звезду благодарю.

Я так не очень понял, но попробую угадать. Если мы хотим чтобы клиент сам опрашивал нашу фоновую задачу, то после её создания (HTTP POST от клиента) возвращаем на клиент её ID (в Quartz у каждой задачи есть свой уникальный ID), потом клиент через HTTP GET с ID этой задачи может через API Quartz получать о ней информацию. Если хотим оповещать клиента по SignalR, то при создании задачи вставляем в неё с DI компонент, через который она (задача) уже когда надо сама отправляет клиенту сообщения. Это для случая Quartz. А Hangfire я не люблю и крайний раз имел с ним дело очень и очень давно, но там как-то так тоже всё похоже. Если я не так понял, то просто опишите словами какой-нибудь сценарий, а я тогда уже точно скажу как его можно реализовать.

Я так не очень понял, но попробую угадать.

В целом, вы поняли правильно. Но есть нюансы.

Если я не так понял, то просто опишите словами какой-нибудь сценарий, а я тогда уже точно скажу как его можно реализовать.

Могу показать код - у меня есть дистрибутив с примерами (ссылка в статье). Но, наверное, это неправильно заставлять человека скачивать код, которым он вряд ли будет пользоваться, поэтому попробую на словах.
Есть страничка, с которой пользователь может отслеживать (опросом) количество неких запущенных им (именно им) фоновых задач (в примере это - задачи, запущенные со страничек других примеров). А также - следить за измененим их числа - запускать триггер, который сработает, когда количество изменений дойдет до указнного (триггер в примере для простоты реализуется на тупом long poll). Вопрос первый - как это реализовать Quartz.net (допустим, вспомогательные классы из примера уже есть)? Вопрос второй - будет ли такая реализация удобной?
PS Если что, то это вот этот пример
PPS Хотя, наверное, из словесного описания непонятно, но эта задача сама никогда не заканчвается (ну, если ее не прекратить извне, закрытие странички прекращает).

Ну, вот, пример (с Quartz) - создание задач, получение списка id задач, остановка задачи по id. Нет только контроля за их количеством, но это легко и уже из кода понятно как можно сделать. Код настолько простой, что я даже Visual Studio не запускал - набивал всё в Code.

Program.cs
using Quartz;
using Quartz.AspNetCore;
using Quartz.Impl.Matchers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddQuartz();
builder.Services.AddQuartzServer();

var app = builder.Build();

//
// Я не хочу заморачиваться с настоящей аутентификацией -
// в демо-целях имя пользователя передаю в Path запроса.
//

//
// POST /user_ololo
//

app.MapPost(
    "/{user}",
    async(string user, ISchedulerFactory schedulerFactory, CancellationToken ct) =>
    {
        // Использовать планировщик по-умолчанию
        var scheduler = await schedulerFactory.GetScheduler(ct);

        // Создаём Job с новым GUID для имени и именем пользователя для группы
        var job = JobBuilder.Create<DemoJob>()
            .WithIdentity(Guid.NewGuid().ToString(), user)
            .Build();

        // Создаём триггер с новым GUID для имени и именем пользователя для группы
        var trigger = TriggerBuilder.Create()
            .WithIdentity(Guid.NewGuid().ToString(), user)
            // триггер должен запустить Job сразу же
            .StartNow()
            .Build();

        // Привязать триггер к Job - это его запустит
        await scheduler.ScheduleJob(job, trigger, ct);
    });

//
// GET /user_ololo
//

app.MapGet(
    "/{user}",
    async (string user, ISchedulerFactory schedulerFactory, CancellationToken ct) =>
    {
        var scheduler = await schedulerFactory.GetScheduler(ct);

        // выбираем все задачи по имени пользователя, которое для каждой
        // задачи является именем группы
        var keys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(user), ct);

        return keys.Select(k => k.Name);
    });

//
// DELETE /user_ololo/83fd9dbb-9aeb-483b-8c72-90dc06247dd8
//

app.MapDelete(
    "/{user}/{jobid}",
    async (string user, Guid jobid, ISchedulerFactory schedulerFactory, CancellationToken ct) =>
    {
        var scheduler = await schedulerFactory.GetScheduler(ct);

        // останавливаем задачу (посылаем ей Cancel)
        await scheduler.Interrupt(new JobKey(jobid.ToString(), user));
    });

await app.RunAsync();

/// <summary>
/// Бесконечная задача.
/// </summary>
class DemoJob : IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        while(true)
        {
            await Task.Delay(1000, context.CancellationToken);
        }
    }
}

Полностью (с *.csproj и прочим) можно посмотреть здесь.

Да, рановато мне джиннам желания загадывать... :-)
Ваш код соответствует тому, что я там написал (почти - есть нюанс, о нем ниже), но в виду я имел совсем другое:

  • задачи, количество, которых надо отслеживать, вполне себе конечные - они имитируют выборку записей из последовательностей (IEnumerable<T> или IAsyncEnumerable<T>), которые перечисляются с задержками (может быть несколько стадий с разными задержками, в т.ч. с нулевой) и выводят их в несколько приемов на страничку. Впрочем прервать их до срока тоже можно (кнопка даже на страничке есть, ну, или страничку закрыть).

  • бесконечная задача - это та фоновая задача, та, которая за ними наблюдает. Вот она завершится только если ее страничку закрыть.

  • функциональность этой задачи - запуск некоего фонового процесса, работу которому пришлось придумывать. Вот я и придумал - учет числа выполняющихся задач. Ее пришлось делать самому: библиотека - не планировщик, в ней этой функциональности нет. А в планировщике такая функциональность, естественно, есть, так что у вас потому просто и получилось, те вспомогательные классы из моего примера вам не понадобились.

А вот чего не увидел (тот самый нюанс) - реакции на завершение задачи, чтобы вернуть этот факт скрипту на страничку через long poll вызов API. Подозреваю, что у планировщика нужная функциональность должна быть, так что это, наверняка, делается. Ну, и ещё, в моем примере возвращался не список задач, а их число, но это исправить элементарно.

Собственно, тот пример был - он не на учет и контроль других задач, а на запуск произвольного процесса в фоне и получение результатов от него в промежутке. Чтобы было не совсем уныло, в качестве такого процесса был выбран учет и контроль выполняющихся задач(в библиотеке они именуются, кстати, исполнителями) определенного типа.

Что нет в функциональности вашего примера с планировщиком - встроенной изоляции одного сеанса от другого, и придется такие вещи доверять скрипту фронта (и проверять, естественно - с учетом возможности CSRF). В обсуждаемой библиотеке это есть сразу за счет интеграции с ISession (котрая работает через недоступную скрипту фронта куку).

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

Вот какие задачи мы решали с помощью SignalR без написания каких-то библиотек, с минимум кода:

Таймер времени на сессию пользователя, который ведётся на сервере. В одном проекте у каждого пользователя есть KPI, который считает сколько он работы проделал за время сессии, и ещё этапы работы автоматически закрываются по периодам, либо по количеству работы, и открываются новые. Всё делается автоматически в контексте сессии пользователя.

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

Похоже на ваши потребности?

Похоже на ваши потребности?

На мои потребности - нет: они - вообще вне контекста какой-либо инженерной задачи.
На возможности библиотеки - похоже.
Но я, вроде как, нигде и не утверждал, что она будет единственно возможным или хотя бы наилучшим решением.

на самом деле сюда SignalR хорошо ложится, не понятно зачем тут серверные сессии. Можно создать Singleton сервис в нём ConcurrentDictionary с активными задачами, внутри задачи отсылаете промежуточные статусы по SignalR в после выполнения задача удаляет себя из списка активных задач. При таком подходе вам не нужно с фронта опрашивать бэк, он сам будет всё присылать, нужно только подписаться на нужное. Так же не обязательно, чтобы промежуточные результаты по задачам прилетали только пользователю который их запустил

на самом деле сюда SignalR хорошо ложится, не понятно зачем тут серверные сессии.
Так же не обязательно, чтобы промежуточные результаты по задачам прилетали только пользователю который их запустил

Вы имеете в виду дргугой сценарий использования, не тот, для которого предназначена библиотека ActiveSession. Она как раз предназначена, чтобы результаты фоновой операции приходили иеннно тому пользователю, который ее запустил: это его личная операция, других она не касается. Зато она не ограничивается сценариями реального времени, для которых предназначен SIgnalR: дальнейший результат можно запрашивать даже просто переходом по ссылке. И получать в ответ хоть статическую страницу совсем без скриптов - а SIgnalR нужен активный скриптовый клиент, поддерживающий постоянное соединение с сервером.

Короче, ActiveSession ориентирована на другой сценарий использования. Насколько такой сценарий использования кому-либо нужен - это другой вопрос. И я в курсе, что этот сценарий далек от магистрального пути, по которому пошел интернет, про это даже в статье есть, в заключительной части.

Глубоко в детали пока не углублялся, но вот чего я не нашёл в содержании, это какого-то исследование альтернатив, или инструментов, которые можно было бы использовать для достижения той же цели. И, самое главное, чем данное конкретное решение лучше.

На поверхности сразу: SignalR, сессия итак создаётся на каждое подключение и держится до его окончания, разные вкладки можно объединять в группы (например, по user-id) и поддерживать процессы для группы. Чем не подходит обычный IHostedService в связке с SignalR и удержание сессионного CancellationTokenSource?

Если говорить про масштабирование вычислений, а это нам точно понадобится, то почему не Orleans? Как-то грустно, когда бекенд, который хостит апишечеку, должен ещё чего-то в фоне делать, да ещё и с лютейшими ограничениями на прилипание пользователя к конкретному экземпляру.

В общем, вопросов много, ответов мало. Если это просто ради опыта, то опыт безусловно это хорошо, но настоящий инженер перед тем, как изобрести колесо, сначала изучит другие колёса.

Глубоко в детали пока не углублялся, но вот чего я не нашёл в содержании, это какого-то исследование альтернатив

А его там и нет. Цель статьи - описание библиотеки, а не ее продвижение. Сама библиотека, по факту - это побочный продукт другой деятельности, про это в статье есть, в конце. Но раз написана - пусть будет, вдруг кому пригодится.для его конкретной ниши.

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

Иначе выглядит это так:

-- Смотрите, какой я написал свой логгер / парсер / запускатель задач / etc..
-- А вы в курсе, что уже есть как минимум решения X, Y, Z... решающих ваши задачи?
-- А зачем? Чукча не читатель, чукча писатель

:)

Чукча не читатель, чукча писатель

Это было сказано в другом контексте: про отсутствие необходимых базовых знаний для обучения профессии. Здесь случай, IMHO другой, Базовые знания, как писать код большими кусками, у меня, смею надеяться, есть (думаю, достаточно даже этой библиотеки, чтобы подтвердить). А в качестве части моего CV на должность техлида - которому по должности как раз положено выбирать средства для использования проекта - эта статья не предназначена.

Полноценность же статьи зависит от задачи. Здесь я ставил задачу полноценно описать получившееся у меня решение, не более того. Добавлять сравнения с альтернативами - это увеличивать объем статьи, а статья и так получилась слишком большой ("редкая птица долетит до ее середины" :-) , это по статистике, любезно предоставленной Хабром, видно).

По предыдущему комментарию дополню: билиотека предназначена для сохранения состояния и выполнения фоновой работы для одного конкретного пользователя. В hosted services из коробки средств для этого AFAIK нет. А вот SignalR как альтернативное средство поддержания сессий, я, возможно, попробую интегрировать. В конце концов, от такого средства библиотеке мало что нужно, в основном - key/value хранилище. А то, что в нем нет HttpContext - это лечится наследованием своего класса (я посмотрел - вроде бы, мешающих этому ограничений нет, сейчас - это вам не ASP.NET Framework).

PS И не бойтесь говорить мне прямо, то, что вы думаете. Меня это задевает не сильно. Ибо в Интернет я пришел ещё в те времена, когда про него говрили "Это Интернет, детка! Здесь могут посласть". И, что характерно, посылали. А до этого у меня в анамнезе были 90-е, хоть и в легкой форме.

Вы зря так реагируете.

В Hosted Services есть всё, что нужно. Это фоновый процесс, который может запускать и останавливать задачи, по одному на каждую сессию. Для коммуникации с фоновым процессом можно использовать простейший диспетчер, например, Task Channels, что также идёт из коробки.

SignalR напрямую решает задачу поддержания активной серверной сессии для каждого клиента, так как это требуется для двух-сторонней коммуникации и обмена данными.

Итого, вы не потрудились провести даже минимальное исследование платформы. Это даже не какие-то "левые" библиотеки, это всё идёт в коробке. Но написали очень много кода.

Зачем? :) Ну возможно в этом и есть смысл, возможно действительно что-то из представленного не решает тех проблем, с которыми столкнулись именно вы. Но какие это проблемы?

Разработка ПО это прежде всего инженерная дисциплина. Писать много кода -- это не значит хорошо, чем меньше кода мы пишем для достижения желаемого, тем эффективней наш труд, тем выше его ценность.

Какого-то анализа правда не хватает. Зачем писать свой велосипед на каждый чих? Потому что могу? :)

В Hosted Services есть всё, что нужно. Это фоновый процесс, который может запускать и останавливать задачи, по одному на каждую сессию.

Может. Но кто будет отслеживать сессии, и в частности - судьбу задач: сессия-то может кончиться внезапно, и кто тогда таймаут проконтролирует? Добавить в требования SignalR - а как быть тем, кто модифицирует уже написанное, SignalR - штука довольно специифическая. Короче, "здесь не всё так однозначно".

Для коммуникации с фоновым процессом можно использовать простейший диспетчер, например, Task Channels, что также идёт из коробки.

Не нашел такого. В том числе - и поиском: утка не ищет. Не кините ли ссылку, чтобы было понятно, о чем речь.?

Но написали очень много кода.
Зачем? :)

Дык, в статье написано - зачем. Не читали, признайтесь?
И требовалось мне (ну, или хотелось, начальства-то надо мной никакого нет, заказчиков - тоже) как раз написать много кода (а ещё - много тестов и много документации).
Ну, а получившаяся библиотека - считайте ее побочным продуктом.

Если вам она не подошла - ну, значит, вам она не нужна. Но, может, кому-нибудь сгодится: для того и статья.

Разработка ПО это прежде всего инженерная дисциплина. Писать много кода -- это не значит хорошо, чем меньше кода мы пишем для достижения желаемого, тем эффективней наш труд, тем выше его ценность.

А если речь идет не о коммерческой разработке, а, к примеру, just for fun - тогда как?

Не нашел такого. В том числе - и поиском: утка не ищет. Не кините ли ссылку, чтобы было понятно, о чем речь.?

Потому что "Task" тут лишний.

https://habr.com/ru/articles/508726/

Не ожидал, что диспетчером можно назвать простую реализацию шаблона "производитель/потребитель" - хотя бы потому, что она сам по себе этот шаблон ничего никуда не распределяет, нужен некий распорядитель (его ещё брокером называют).

В Hosted Services есть всё, что нужно.

Сам по себе Hosted Service имеет только очень базовый функционал для выполнения кода в фоне. Если же вам нужно еще как-то с этим кодом взаимодействовать, управлять его запуском или остановкой, или выполнением в каком-то контролируемом числе потоков - то все это вам придется делать самим, и это уже проходили. В готовых же frameworks все это уже есть. А под капотом там как раз и есть Hosted (Background) Service, просто уже с различными готовыми "добавками".

И ваш любимый планировщик Quartz интегрирован в ASP.NET Core именно как Hosted service, я правильно понимаю (если нет - поправьте),

Тесты — дело формальное. Их написание порождает материальные следы ("артефакты"). Тогда как проверка может проводиться даже просто заданием значений в отладчике, делаться совершенно неформально и следы она за собой оставлять не обязана. ...

А ведь так оно и есть! Только за эту формулировку можно плюс поставить, по моему.

Не нравится мне опора на UseSession. Состояние сеанса в ASP.NET - это целое хранилище сериализуемых данных, которое может храниться в куче мест, включая память, реляционные СУБД и redis. И из всего этого комбайна вам требуется... что? Одно единственное значение или вообще идентификатор сессии?

Вторая проблема подобной связи - во времени жизни. Время жизни фоновой операции совершенно не обязано совпадать с пользовательским сеансом, и может отличаться в обе стороны. Некоторые операции (скажем, загрузка файла с регулярными отчётами о прогрессе) актуальны только в том окне, где были инициированы, и весь пользовательский сеанс для них - слишком много. Другие операции (скажем, синхронизация с другой системой) актуальны для инициировавшего их пользователя или даже для группы пользователей, и пользовательский сеанс для них - слишком мало.

Вообще, я в своей практике ещё ни разу не встречал таких данных, которые и правда были бы уместно хранить в состоянии сеанса. Он всегда либо слишком большой, либо слишком маленький, и его использование - всегда костыль.

И из всего этого комбайна вам требуется... что? Одно единственное значение или вообще идентификатор сессии?

Из всего этого комбайна мне требуется хранилище пар key-value (значения, в основном, строковые). Пар этих там может быть довольно много. Базовая часть идентификатора активного сеанса, номера поколений активных сеансов (активный сеанс пока один на сенанс ASP.NET, но внутри заложена и скоро будет реализована возможность нескольких, с суффиксами в идентификаторах в зависимости от параметров запроса (из коробки - по соответствию пути регулярке) ), хост, на котором выполняется исполнитель (пока - чисто для контроля, правильности настройки sticky session), а исполнителей может быть много. Есть (и с самого начала были) планы сделать всё это распределенным, и там пригодился бы распределенный характер хранилища сеанса, но пока что это только планы.

Некоторые операции (скажем, загрузка файла с регулярными отчётами о прогрессе) актуальны только в том окне, где были инициированы, и весь пользовательский сеанс для них - слишком много.

В активном сеансе может выполняться несколько независимых исполнителей , по одному на операцию. Операция кончилась - исполнитель завершился. Опять-таки, можно прекратить активный сеанс целиком. Разные группы путей запроса - можно завести несколько разных активных сеансов (это скоро будет). Ну и таймауты у активных сеансов и у исполнителей свои, их можно сделать меньше, чем таймаут сеанса ASP.NET, чтобы зря память и процессор не ели.

Другие операции (скажем, синхронизация с другой системой) актуальны для инициировавшего их пользователя или даже для группы пользователей, и пользовательский сеанс для них - слишком мало.

Такие операции - они просто вне области применимости этой библиотеки: она не претендует на роль универсального решения, пригодного для реализации фоновых операций в интересах всего приложения.

из коробки - по соответствию пути регулярке

А можно без регулярок всё-таки? Ну есть же понятие конечной точки и её метаданных, нужно лишь разобраться как с ними работать...

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

Ага, и разные окна имеют (потенциальный) доступ к ним всем, независимо от того где кто был запущен. А это - потенциальный источник багов.

И при чём тут вообще сеанс?

А можно без регулярок всё-таки? Ну есть же понятие конечной точки и её метаданных, нужно лишь разобраться как с ними работать...

Можно. В основе там - предикат, принимающий HttpContext (он есть уже сейчас, просто это прошло мимо обеих статей). Как с метаданными работать, я знаю. Но надо подумать, как задавать фильтр при конфигурировании (в стиле UseEndpoints - не хочется). Благодарю за идею.

А это - потенциальный источник багов.

Всё, что опирается на куки, будет ровно таким же источником багов.

И при чём тут вообще сеанс?

ASP.NET - хранилище пар ключ/значение
IActiveSession - контейнер сервисов, прежде всего, чтобы брать оттуда Scoped-сервисы. Ну, и ещё кое-что, по мелочи.

Но надо подумать, как задавать фильтр при конфигурировании (в стиле UseEndpoints - не хочется).

Атрибутами на контроллерах и на действиях, как ещё?

Всё, что опирается на куки, будет ровно таким же источником багов.

Ну да, вот потому и нужны альтернативные способы учёта задач. Например, идентификатор можно передавать в заголовке HTTP. Или вовсе включить как часть маршрута.

ASP.NET - хранилище пар ключ/значение IActiveSession - контейнер сервисов [...]

Вот отсюда и вопрос. С фига ли они вообще связаны друг с другом?

Атрибутами на контроллерах и на действиях, как ещё?

А как быть с Minimal API? Короче, подумать тут действительно надо.

Ну да, вот потому и нужны альтернативные способы учёта задач. Например, идентификатор можно передавать в заголовке HTTP. Или вовсе включить как часть маршрута.

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

Ну, а ещё вот лично я привык к возможности работать с одним сайтом из нескольких окон сразу. На базе куки несколько окон в едином сеансе получаются автоматически.Ну, а исполнитель (т.е. задачу) вполне можно привязать и к конкретному окну: в проекте с примерами так и сделано.

Вот отсюда и вопрос. С фига ли они вообще связаны друг с другом?
Так было проще сделать изначально: механизм сеансов ASP.NET Core - он стандартный для привязки запроса к сеансу, уже есть, ничего колхозить не надо. Опять же, схему с несколькими исполнителями в единой сессии (например, по одному на окно) с этим механизмом делать удобно - а мне такая схема работы с сайтами, как я писал выше, нравится.

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

В целом, таково проектное решение в данной библиотеке. Минусы у него есть, но и плюсы - тоже. Можно сделать и по-другому, но это, наверное, будет уже другая библиотека.

Ну, а исполнитель (т.е. задачу) вполне можно привязать и к конкретному окну: в проекте с примерами так и сделано.

Осталось понять, зачем этой задаче/исполнителю нужен остальной сеанс.

В этой библиотеке? Здесь как минимум, запрос из браузера привязывается к сеансу, а работающий исполнитель уже ищется через него. Это попадает под категорию "нужен". А ещё можно поинтересоваться "чем полезен": может хранить общие обхекты
В принципе? Можно обойтись. К примеру, автор той статьи (май 2022, ссылка в моей статье есть), из которой я взял исходную идею, вообще вместо сеанса держал в кэше ссылку контейнер сервисов (это не совсем правильно - штатно держать полагается ISessionScope, и очищать(Dispose) его, но не суть). И сеанса как такогого у него изначально не было, а задачей служил Scoped сервис из этого контейнера. Правда, потом, уже после публикации статьи, он наткнулся, видимо, на параллельный доступ к чему-то типа DbContext, которому от параллельного доступа может поплохеть, и добавил сессию, которой кроме контейнера была ещё и блокировка - чтобы исключить параллельный доступ к такому сервису из обработчиков разных запросов.
Ну, или тут первый комментатор разъяснял, как задача запускается из планировщика.

Обобщая: в принцепе - можно обойтись, но - так сделано, и из этого можно извлечь даже пользу. Проблему поиметь, правда, тоже можно.

Непонятно, почему вы так против ISession? Я понимаю, что это не стильно, не модно и не молодёжно - т.е., что MS продвигает дргие решения, вроде SignalR и Blazor, но я не понимаю, зачем обязательно следовать моде? Особенно - в вещах некоммерческих, вроде этой библиотеки?

В этой библиотеке? Здесь как минимум, запрос из браузера привязывается к сеансу, а работающий исполнитель уже ищется через него. Это попадает под категорию "нужен".

Нет, это попадает под категорию "зачем?"

Непонятно, почему вы так против ISession? Я понимаю, что это не стильно, не модно и не молодёжно - т.е., что MS продвигает дргие решения, вроде SignalR и Blazor, но я не понимаю, зачем обязательно следовать моде? Особенно - в вещах некоммерческих, вроде этой библиотеки?

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

Нет, это попадает под категорию "зачем?"

Считаете, что я не в курсе альтернатив? Это неверно.
Считаете, что делать так не надо? Я вас услышал.
Считаете, что нужно все делать единственно правильным образом? Я не согласен - альтернативы стоит, как минимум, проверять.

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

Я ознакомился с вашим мнением, но ему не доверяю: "я в своей практике ещё ни разу не встречал" - слабое основание для столь категорических выводов. Вряд ли вы можете знать всё про применимость в столь широкой области как программирование для веб.
К примеру, я тут не так давно по всяким своим делам рылся в OWASP'овских рекомендациях по защите от CSRF, и случайно обнаружил, что некогда, во времена WebForms, упомянутые сеансы служили штатным способом защиты от этой самой межсайтовой подделки запросов. Ну да, это уже устарело, современные средства хранят и передают защитный маркер по-другому. Но если бы кто-то работал с WebForms с аутентификацией по кукам и был бы при этом озабочен защитой от CSRF, то он, в отличие от вас, в своей практике столкнулся бы с необходимостью использования сеансов ASP.NET.
Поэтому я считаю вашу категоричность необоснованной.

И, как я писал в самой статье, применимость библиотеки меня беспокоит мало. Денег за нее я получить не собираюсь, так что могу протестировать с ее помощью и немодное решение. Ну, а быть мейнстримом она все равно не предназначена: я отлично понимаю, что раскрутить ее я не смогу.

пока живо сетевое соединение это не особо отличается от асинхронных запросов

так?

а вот если с разных устройств или сессий по одному user id можно получить результаты длительного запроса то да, интересно

С разных - нельзя: привязка идет по сессии.
Но держать при этом живое сетевое соединение с одного устройства постоянно не обязательно. Можно интересоваться прогрессом весьма изредка, главное - не слишком редко, чтобы сессия по таймауту не отвалилась.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации