Задать вопрос
Ответы пользователя по тегу C#
  • Произойдет ли сортировка отфильтрованных элементов или наборот?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Объект, который вернет IEnumerable.OrderBy, сначала выберет всё, что вернет Where, а потом это отсортирует. Произойдет это в момент, когда будет вызван его метод GetEnumerator - напрямую или как часть реализации оператора foreach и т.п. - в этом и состоит его отложенное выполнение.

    Вообще, методы LINQ, которые выполняются в отложенном реэиме, делятся, в свою очередь на две группы: работающие в потоком режиме (потоковые, streaming execution) - они обрабатывают каждый элемент входной последовательности по отдельности и сразу по получении - и не-потоковые, работающие с последовательностью как целым.
    Where относится к первой группе, OrderBy - ко второй.
    Ответ написан
    Комментировать
  • Как реализованы асинхронные методы c#?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вот вам перевод (часть первая, а всего там шесть частей) фундаменнтальной статьи по async/await одного из разрабочиков .NET в Microsoft (а именно - Stephen Toub).
    Читайте и просвещайтесь.

    PS Автор вопроса никак не желает считать текст выше ответом на свой вопрос - а зря. Потому что по-другому на форуме на него не ответишь - слишком много букв писать надо. Но сегодня - выходной, так что - попробую.
    И самое главное, что вам следует осознать - что код асинхронного метода, который вы написали, и код машины состояний, в которой он преобразуется (и далее - транслируется в IL и компилируется в команды процессора) - это два разных кода, пусть и эквивалентных.
    Если очень вкратце:
    • компилятор преобразует код асинхронного метода до неузнаваемости, в ту самую "машину состояний": класс, в котором есть метод, ее реализующий, и есть поля для данных, как нужных для работы самой машины состояний (в частности - для хранения состояния), так и для хранения локальных переменных исходного метода (да-да, в стеке они не хранятся);
    • каждый кусок исходного метода - начальный, между двумя await и завершающий - преобразуется в отдельный кусок метода машины состояний, выполняемый, когда выполнение машины состояний возобновляется в определенном состоянии; этот код производит действия, определенные в этом куске метода, и либо меняет состояние на новое значение и переходит к ожиданию (там, где в исходном асинхронном методе написан оператор await), либо завершает выполнение метода машины состояний;
    • для выполнения перехода к ожиданию используется объект awaiter'а, получаемый методом GetAwaiter() операнда операции await;
    • перед переходом к ожиданию проверяется возможно продолжение без ожидания, если это возможно (метод IsCompleted() awaiter'а возвращает true), то ожидания не происходит, а возобновляется выполнение машины состояний уже для нового состояния;
    • запуск ожидания происходит во внешнем относительно асинхронного метода коде - в объекте awaiter'а;
    • и при завершении метода машины состояний, и при запуске ожидания метод машины состояний возвращает управление в код, который вызвал в этот раз метод машины состояний (для одного вызова асинхронного метода вызовов метода машины состояний может быть много, см. дальше);
    • первый возврат из метода машины состояний происходит в вызывающий метод, он возвращает объект задачи, позволяющий отследить в вызывающем методе завершение работы машины состояний ( и тут есть нюанс, связанный с возвращением ValueTask, а не Task);
    • при окончательном (а не при запусе ожидания) выходе из метода машины состояний этот возвращенный объект задачи завершается;
    • после завершения ожидания метод машины состояний вновь вызывается: в типичном случае - в свободном потоке из пула потоков, но возможны варианты (какие - курить тему Synchronization Context); и он продолжает свое выполнение для нового состояния

    Это - максимально урезанное описание работы асинхронного метода.
    Если вы смогли из этого описания понять ответ на ваш первый вопрос - хорошо. Если нет - читайте статьи(ссылка выше), или книги (лично я рекомендую главу про асинхронные меотоды из книги Рихтера "CLR via C#").
    Касательно второго вопроса. Задачи, выполнение которых ожидает асинхронный метод, будут выполняться совершенно параллельно, каждая - в своем потоке из пула, возможно - на разных ядрах/процессорах). А асинхронный метод в это время не будет выполняться совсем: поток в котором он выполнялся, будет дальше выполнять код после его вызова в вызвавшем его методе (или код, возвращий поток в пул, если это был вызов машины состояний после ожидания). А после завершения обеих этих задач выполнение метода машины состояний, в который был преобразован асинхроннный метод, будет возобновлено и задача, которую возвратил асинхронный метод, будет завершена.
    Ответ написан
    Комментировать
  • Почему данные из представления не передаются в модель в ASP.NET?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Источник проблемы с привязкой действия: в теле ответа у вас нет ничего, кроме antiforgery token. Так что надо смотреть на клиенте HTML, который сгенерил движок представления - похоже, кривизна там, форма сгенерена неверно.
    Перед просмотром имеет смысл включить для окна/вкладки, где будет страница с формой, средства отладки браузера (F12) и смотреть ошибки, которые они выведут - может, они что найдут в авттоматическом режиме.

    Конкретную причину ошибки я пока не опознаю. Подозрение у меня вызывает атрибут asp-for=Id для первого элемента формы: значения атрибутов tag helper чисто по синтаксису надо указывать в кавычках, потому что они подчиняются тому же синтаксису, что и обычные атрибуты HTML. Но в этом ли причина, утверждать не могу.
    Ответ написан
  • Можно ли использовать Expression без типов-делегатов?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вкратце: не заморачивайтесь.
    В ремесле программирования есть полезный принцип, обозначаемый англоязычным сокращением YAGNI, что в переводе означает "Вам это не понадобится." Согласно этому принципу, раз вы не знаете, как можно использовать Expression с параметром-типом не являющимся делегатом, то вам вряд ли понадобится его так использовать.
    А проверки параметра-типа там нет, скорее всего, потому что эта функциональность (она называется Expression trees) появилось давно, больше 15 лет назад, в .NET Framework 3.5, а тогда в .NET проверки параметра-типа ЕМНИП не было.
    Но, подозреваю, что попытка использовать в качестве TDelegate не тип-делегат просто приведет к ошибке: почти наверняка - при попытке получить исполняемый код из выражения (метод Compile), а, возможно, и раньше - или при попытке создания объекта, или даже на этапе написания кода или компиляции: ее может (да или нет - не проверял) обнаружить анализатор при компиляции исходного кода и даже IntelliSense. И подозреваю, что при попытке обойти дерво такого объекта Expression tree (это - альтернативный способ использования этой функциональности), тоже будет ошибка - ибо выражение получается бессмысленным.
    В любом случае, истиной в последней инстанции является исходный код. Если вам так уж захотелось узнать, что будет - читайте исходный код: он лежит на GitHub, ссылка есть в документации по классу в learn.microsoft.com (сразу предупреждаю: там все сложно).
    Ответ написан
    Комментировать
  • Как реализовать потоковое заполнение массива другим потоком?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Сколько там недочетов. потенциальных ошибок. неверных решений?

    1. У вас намешаны синхронная - в с одном потоке с блокировками (Wait, Sleep) - и асинхронная (async/await) многозадачность Теоретически, совмещать их можно, но лучше остановиться на каком-то одном подходе - легче будет. Синхронное выполнение задач (каждая - в своем потоке, который она блокирует по необходимости) - это проще, но асинхронное выполнение (при которм поток не блокируется, а освобождается, а задача планируется для продолжения после завершения операции в свободном потоке) позволяет получить больше производительности. Вот и выбирайте, что вам нужнее.
    2. Для синхронизации используйте не переменные (а, к примеру, isRun у вас используется именно для этого), а объекты синхронизации. В частности, isRun следует заменить на объект синхронизации. Для синхронного выполнения лучше всего IMHO подойдет ManualResetEventSlim. для асинхронного - что-нибудь, на чем можно сделать await (например, TaskCompletionSource.Task).
    3. Любой доступ к совместно используемым в разных задачах объектам(ресурсам) следует синхронизировать путем захвата связанного с ресурсом объекта исключающего доступа. Для синхронного выполнения подойдет оператор lock (вижу, что вы его уже используете). Для асихронного выполнения lock можно использовать, но - только если в его теле нет await (и компилятор вам не позволит нарушить это правило). Не пытайтесь обойти это правило, используя вместо lock другие объекты синхронизации, подразумевающие захват владения на уровне потока (Monitor, Mutex...) - компилятор всего лишь не сможет вам помешать сделать ошибку. В таком случае для синхронизации можно использовать SemaphoreSlim, но это требует понимания и аккуратности, поэтому подробности пока не пишу.
    Думаю, пока этой информации к размышлению вам хватит.
    Ответ написан
    1 комментарий
  • Поиск куда можно добраться по графу за время?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Алгори́тм Де́йкстры (англ. Dijkstra’s algorithm) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных.

    Здесь
    Ответ написан
    Комментировать
  • Как в C# в динамике задать у обощенного класса тип поля хранимого значения?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Для задания динамического типа для поля в C# есть ключевое слово dynamic. Но на самом деле, это - object, просто для него компилятор автомтически реализует преобразование типов (и ещё кое-что - см. [документацию](https://learn.microsoft.com/en-us/dotnet/framework...) она хотя и по Framework, но многое из этого есть и в .NET(Core и современном))
    Ответ написан
  • Возможно ли решить по другому вывод информации?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Простейший способ решить вашу задачу - найти того, кто ее вам решит: человека - за деньги, по дружбе и пр. - или ChatGPT и т.п. LLM. Человека искать - это не здесь (если на Хабре, за деньги - то есть раздел Freelance).

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

    В частности (я не телепат, но попробую угадать), если вам мешает неудобная работа с TimeSpan, то стоит вспомнить (или узнать), что он представляет собой число фиксированных мелких промежутков времени, именуемых tick. Вы можете прочитать это число в его свойстве Ticks, работать с ним как с обычным числом - не только сравнивать, но и умножать, делить, складывать, вычитать - а потом, если надо, перевсти обратно в TimeSpan методом TimeSpan.FromTicks.

    PS Если будете описывать задачу, лучше всего пользуйтесь не автоматически даваемыми названиями элементов интерфейса - dateTimePicker2, textBox6 и пр. - а названиями указывающими на их смысл - лучше всего их переименовать соответствующим образом, это - крайне рекомендованная практика при программировании вообще.
    Ответ написан
    2 комментария
  • Как использовать Bitmap в .NET в 7.0?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Либо использовать для проекта Windows Forms, либо явно добавить в проект нужный пакет System.Drawing.Common
    Ответ написан
  • Стоит ли разделить микросервис Account на микросервис по авторизации и микросервис пользователей?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    микросервис должен выполнять только один функционал

    Микросервис никому ничего не должен. Даже - существовать отдельно от приложения.
    И в вашем приложении, скорее всего, он тоже не нужен, а список пользователей и авторизацию можно сделать частью самого приложения. Но если вы хотите потренироваться с микросервисной архитектурой, то смотрите по своей задаче.
    Если у вас нет пользователей, которые просто числятся и никогда не авторизуются, то отдельный микросервис для пользователей вам точно не нужен. Потому что в таком случае атрибуты авторизации - это неотъемлемая часть сущности "пользователь".
    В противном случае возможны варианты, но нужно взвесить все за и против. В частности - возможную потерю целостности, когда какие-нибудь авторизационные данные не будут соответствовать ни одному пользователю.
    Ответ написан
    Комментировать
  • Батчинг входящих запросов с неблокирующим ожиданием?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    А вам точно это нужно?
    Потому что тут у вас появляется нюанс, который не совсем ложится на схему работы веб-приложения: у ваших отдельных запросов появляется общее состояние. Как минимум, это - накапливаемый пакет запросов, а ещё, наверное, в это состояние входит нечто общее для всех запросов для работы с БД: подключение или, если используется Entity Framework, DbContext. Это общее состояние придется как-то хранить, регулировать доступ к нему (DbContext к примеру, параллельный доступ не поддерживает в принципе), и вовремя это состояние удалять. Если посмотреть на стандартные механизмы ASP.NET Core, то сессия (ISession) для этого, наверное, не подойдет - там можно хранить только сереализуемые в байты пассивные объекты, и насчет регулировки доступа там непросто. Подойдет концентратор (Hub) SignalR, у которого есть сохраняемый между вызовами контекст подкючения - но ради него, скорее всего, потребуется менять способ вызова API из браузера: у него там своя клиентская библиотека.
    Ну и, по-любому, как-то надо реализовывать активную часть - которая, собственно, отслеживает пакет изменений и вовремя отправляет его в БД.
    Ваша идея
    Шедуллер в фоне будет периодически читать коллекцю объектов на запись и устанавливать результат выполнения в соотвтвующий TaskComplitionSource.

    мне не совсем нравится. Зачем периодически? Kучше чтобы эта активная часть срабатывала по факту добавления запроса в пакет - например, асинхронно ожидала Task от TaskComplitionSource. который метод добавления запроса завершал бы по факту добавления завершающего пакет запроса. Но и завершение по таймауту тоже предусмотреть надо - по жизни оно всякое бывает: обычно для таких целей используется WaitAny для комбинации основной ожидаемой задачи с Delay по таймауту.
    Ну, а ещё требуется, наверное, чтобы для каждого пользователя состояние было свое. В принципе, это делается, но надо делать. Для SignalR для этого можно использовать Hub.Context.Items - это словарь, который может содержать произвольные объекты, и сохраняется на время действия всего подключения.

    А ещё у меня, в принципе, есть своя самодельная библиотека, которая решает ту же задачу - сохранение контекста сеанса, в том числе - активного, с выполняющимся кодом. Я описывал ее недавно в статьях (кроме основной статьи есть дополнительная) на Хабре. Можете попробовать её, если переделывать API не хочется: она вполне годится для работы с API на базе MVC API Controller. или Minimal API. В принципе, она заточена немного под другую задачу - получние и возврат дополнительных результатов в фоне, но для вашей задачи она тоже подойдет. Напишу тут сразу технические подробности как использовать: ссылку на активную часть, собирающую и отправляющую пакет, можно хранить в IActiveSession.Properties, точно так же, как если бы вы хранили ее в Hub.Context.Items, а обработчик завершения, который прибирает за собой (в SignalR его место в OnDisconnect) - привязать к IActiveSession.CompletionToken через его метод Register. В общее для всез запросов состояние входит свой контейнер сервисов со своей областью действия в течение всего существования состояния, так что, если для работы с БД требуется Scoped-сервис из DI-контейнера, то его можно получить оттуда (в дополнительной статье написано, как, а также написано, как защититься от нежелательного одновременного доступа к такому сервису).

    Только вот библиотека эта, естественно, ни разу не стадартная, use at your own risk. Но если попробуете ее, мне будет интересно, что получилось. В том числе - и обнаруженные ошибки, заодно я и для вас их исправлю :-) .

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

    PPS А ещё благодарю за идею, о том, в какую сторону мне развивать мою библиотеку.
    Ответ написан
    1 комментарий
  • Как выполнить функцию после завершения всех задач?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Используйте метод Task.WaitAll
    Ссылки на все эти задачи, при этом надо, кончено не бросать без присмотра при перезаписи переменной outer, а сохранить (например, в массив Task[])
    Ответ написан
    Комментировать
  • Как правильно разделить логику между контроллерами с usecases?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Абстрактно "правильного" варианта реализации нет, всё зависит от конкретных деталей.
    В частности, посмотрите, используют ли ваши методы действий одни и те же внешние зависимости, или каждый - свои. Во втором случае некотоый смысл разнести их по разным контролерам есть: снизить сложность, которую дают лишние, не обусловленые задачей связи (coupling). А первом случае - нет (но может и появиться по другим основаниям).

    А вся эта теория "чистой архитектуры" - она абстрактная. Ее полезно знать, чтобы иметь возможность рассмотреть больше вариантов решения, и видеть больше про возможных подводных камней, а вот фанатично ей следовать не стоит (если, конечно, ваша задача - сделать что-то полезное, а не спихнуть курсовик и т.п.).
    Ответ написан
    Комментировать
  • Как дождаться выполнения асинхронного метода?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    SomethingFuncCatch().Wait();
    В Wait дополнительно можно передать таймаут и/или CancellationToken (см. описание метода Task.Wait ).

    Только вот, боюсь, что вы не дождетесь когда закончится цикл
    while (i > 0)
    {
        Console.WriteLine("NewProcess started");
        i = i--;
    }

    Ибо вы в нем пытаетесь присвоить i то значение, которое было до уменьшения на 1.
    Ответ написан
  • Нормально ли использовать var вместо явной типизации в C#?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вынесено из комментариев.
    Василий Банников , ну, что вам на это сказать?
    Сильно легче код писать таким образом.
    Писать - да, читать - it depends, как говорят англичане.

    Если тебе сложно понять смысл кода без типов - значит кто-то плохие имена дал переменным.
    А разве в legacy 4-5-летней выдержки, над которым поработало много программистов, хороших и разных, по-другому бывает? А ещё имена могут дать хорошие, понятные - но основанные на системе понятий, которая нетривиальна и не попала при этом в документацию. Чтобы понять, о чем я - попробуйте почитать исходники подсистемы маршрутизации в ASP.NET Core. лучше всего - после работы с чем-нибудь типа MVC или Razor Pages: сомневаюсь, что вы сразу въедете, что Model там и Model тут - это разные Model.

    А ещё бывает, что методы называются почти одинаково, но типы значений у них разные. Пример: builder.Services.AddOptions() и builder.Services.AddOptions<TOptions>() из кода конфигурирования контейнера сервисов, методы расширения IServiceCollection для Option Patterns - типы возвращаемых значений у них разные. А их ещё любят цеплять друг к другу к таким же методам через точку - вот и смотри потом, чой-то в ConfigureOptions какие-то лишние параметры вылезли.

    с анонимными типами без var работать было бы очень сложно.
    Правильный вопрос - зачем вообще заставлять работать с анонимными типами? Подозреваю, что их из-за маршрутизации в MVC завезли - как типы параметров, вместо того, чтобы передавать в методы генерации ссылок вместо IDictionary<String,Object>, как это оно там реально требуется. Но, в Microsoft решили, видимо, тогда не делать инициализаторы для словарей (при том, что потом всё равно сделали, не отвертелись), а притащить вместо них анонимные типы (интересно откуда - неужели прямо из JS?). Но это не точно. (Ага, в комментарии мне напомнили про LINQ)

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

    Ну и помни ещё, что ты же не в блокноте код пишешь
    Но читаешь-то ты его не только в IDE. В частности, MS в своей документации на сайте в примерах очень любит писать var - вот и выбирай потом, что делать: лезть прямо сразу в документацию по API (а ссылки отнюдь не прилагаются) или рисковать упустить что-то важное? А если ещё учесть, что сейчас принято документацию ограничивать примерами (MS любит, да) - оно вообще весело.

    Но, с другой стороны, автор вопроса, похоже, с трех- и более этажными дженериками ещё не сталкивался (ну, или забыл, как они выглядят) - там без var тоже грустно.
    Как-то так
    Ответ написан
    2 комментария
  • Миграция вылетает по Stack Overflow. Есть ли ограничение на объем данных в миграции?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Причина ошибки, как пишется в сообщении о ней - переполнение стека. Размер стека для потогков определяется процессом, в котором работает приложение. Обычно он фиксирован и записан в заголовке .exe. Если при размещении веб-приложения в своем процессе (так делается запуск из Visual Studio) стека хватает, то настройте размещение приложения вне процесса и в IIS: документация - здесь, по умолчанию AFAIK для IIS используется размещение приложения ASP.NET Core внутри его рабочего процесса, и используется размер стека этого рабочего процесса.

    PS А вообще, я бы на вашем месте пересмотрел код миграций и использовал стек (локальные переменные методов имеющие тип значений, память, отводимая через stackalloc и т.п.) в нем по минимуму. Потому как требования к памяти приложения имеют свойство расти со временем, а стек задается фиксированной величиной.
    Ответ написан
    Комментировать
  • Как добавить одно поле множеству классов без применения наследования и добавления поля в каждый класс по отдельности?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Привыкли к миксинам в языке с динамической типизацией? В C# их нет, и вряд ли будут, потому что типизация тут статическая. И приемы программирования - соответственно, другие.

    В частности, чтобы добавить дополнительное свойство (или метод) в классы из разных иерархий наследования, используйте интерфейс. Примерно так (примеры - на основе кода из комментариев):
    public interface IChosable {
        bool IsChosen {get; set;}  //Желательно имена писать таки на английском правильном
                                                 // а не ломаном. А то глаза режет.
                                                 //Ну, или хотя бы - по русски транслитом: 
                                                 // Vybrano вместо IsChoised - и то лучше, IMHO
    }
    
    //...
        public class ViewedDirectoryData : DirectoryDataEntity, IChosable
        {
            public bool IsChosen {get; set;}  
        }
    
        public class ViewedOutputData : OutputData, IChosable
        {
            public bool IsChosen {get; set;}  
        }

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

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

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вы неправильно понимаете класс enum. enum - это не класс, а ключевое слово языка C#.

    PS
    И я задумался:
    Надо не думать, и даже - не знать. Надо - уметь применять. Подумайте, какую практическую задачу вы хотели решить этой задумкой? Если такой задачи нет - не забивайте себе голову. А если желаете забить себе голову чисто для себя чем-нибудь абстрактным, то для этого лучше всего подойдет философия, а не IT.
    Ответ написан
    Комментировать
  • Как сделать Mock на приватное поле типа Dictionary для Integration tests или как можно проверить, что его метод был вызван?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Сделайте фабрику для экземпляра, реализующего кэш, и передавайте через конструктор её, в виде интерфейса. Тогда кэш у вас будет частным, действующим только внутри класса, но конкретная его реализация будет задаватьс снаружи. И все будет по фэн-шуюSOLID
    Для теста передайте в конструктор имитатор ("мок") этой фабрики, который создает и возвращаетнужный вам имитатор кэша.

    PS Стндартная для .NET имплементация кэша в памяти - это MemoryCache, он реализует интерфейс IMemoryCache. Только вот самому его имитировать нетривиально, поскольку у него есть особенность в поведении: его элемент (ICacheEntry) сохраняется в кэше по факту очистки (вызову Dispose() ). Я про это даже статью на Хабре написал.
    Ответ написан
    2 комментария
  • Как предотвратить изменение пользователем данных во время выполнения async метода сохранения этих данных?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Простейший вариант: копируйте (синхронно) в обработчике нажатия кнопки данные, которые вы сохраняете, в другое место, а там пользователь пусть хоть обизменяется: на сохраняемые данные это уже не повлияет. Если данных вдруг много, то тогда уже придется синхронизировать доступ к ним (для асинхронной синхронизации подходит SemaphoreSlim.WaitAsync/Release, а если вы можете позволить себе блокировку потока UI, то и lock сойдет). Есть и более сложные схемы - копировать только те данные, которые пользователь собрался изменить, и сохранять в таком случае именно их, а не данные из ViewModel ("копирование при записи"). Короче, дерзайте: вариантов много.
    Ну, а вариант с ползунком сам по себе ненадежен: пользователю доверять нельзя. Но как подспорье, вместе с блокировкой изменения - годится.
    Ответ написан
    Комментировать