Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
1 / 1 / 1
Регистрация: 07.03.2018
Сообщений: 98
.NET 10

Как правильно реагировать на отмену операции пользователем?

23.02.2026, 20:39. Показов 721. Ответов 11

Студворк — интернет-сервис помощи студентам
У меня есть метод-обёртка для асинхронных операций, который управляет состоянием (Begin/EndOperation) и обрабатывает исключения. Пользователь нажимает кнопку – запускается ExecuteAsync. Если он нажимает кнопку снова, первый вызов прерывается исключением OperationCanceledException, а второй стартует.

Поскольку отмена ожидаема и инициирована самим пользователем, я не хочу показывать ему сообщение об ошибке. Но оставлять блок catch пустым кажется неправильным.

Сейчас я возвращаю Errors.Common.OperationCancelled, но тогда в вызывающем коде приходится каждый раз проверять, не является ли ошибка отменой, чтобы скрыть уведомление. Возможно, есть более элегантное решение?

Упрощённая версия метода:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public async Task<Result<T>> ExecuteAsync<T>(
    Func<CancellationToken, Task<Result<T>>> operation,
    CancellationToken cancellationToken = default)
{
    BeginOperation();
 
    try
    {
        return await operation(cancellationToken);
    }
    catch (OperationCanceledException)
    {
        return Errors.Common.OperationCancelled;   // сейчас так
    }
    catch (Exception ex)
    {
        return Errors.Common.Unexpected(ex.Message);
    }
    finally
    {
        EndOperation();
    }
}
Вопрос: Как лучше обрабатывать OperationCanceledException?

P.S. Также интересуют общие рекомендации по оформлению catch (Exception ex) в подобных методах-обёртках: что логировать, возвращать ли пользователю ex.Message или лучше обобщённое сообщение, стоит ли перехватывать Exception вообще?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
23.02.2026, 20:39
Ответы с готовыми решениями:

Сделать так что бы на событие mouseEnter реагировал только canvas, но не реагировали находящиеся в нем компонеты
вдруг &lt;Canvas.Triggers&gt; &lt;EventTrigger...

Как отменить запуск анимации в Style.ItemTemplate
Здравствуйте. Стоит такая задача: создается ListBox на основании шаблона из ResourceDictionary: ...

Как простым способом отменить изменения в ячейках DataGrid?
Нашел в инете код: bool isEditEnding = false; private void...

11
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
23.02.2026, 21:44
Цитата Сообщение от Stormhead Посмотреть сообщение
Возможно, есть более элегантное решение?
А что вы хотите получить?

В концепции MVVM:
1) Есть Model в которой инкапсулирована вся Бизнес (Доменная) Логика.

2) Есть View в которой вся Логика представления. Здесь должны быть описаны все правила и требования к пользовательскому интерфейсу. В данном случае:
1. Пользователь нажимает кнопку и ожидает такого результата.
2. По получению результата, происходит (в интерфейсе) то-то и то-то. Например, обновляется представление такого-то региона, или происходит смена представления (переход на другую страницу) и т.д.
3. Если пользователь нажимает кнопку до получения результата, то происходит то-то. Например, выплывает окно с предупреждением, или прогресс выполнения сбрасывается на начало и т.п.
3) К уже имеющимся Model и View, с уже сформированными требованиями и правилами для потребителя Model и поставщика данных для View, создаётся ViewModel функция которой только одна: отразить Model в удобном для View виде.


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

В данном случае, возможные решения, которые на вскидку пришли мне в голову:
- Окно предупреждения перед перезапуском выполнения;
- Окно уведомление о перезапуске;
- Сброс прогресс бара;
- Логирование ошибки.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
24.02.2026, 09:16
Цитата Сообщение от Stormhead Посмотреть сообщение
Упрощённая версия метода:
Это в Model или в ViewModel ?
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3439 / 2759 / 575
Регистрация: 04.09.2018
Сообщений: 8,660
Записей в блоге: 3
24.02.2026, 12:04
Stormhead, многое будет зависеть от того, как и какие исключения обрабатываются в самих 'operation'. Если в какой-то из переданных operation нет обработки исключений, то данный метод-обёртка тоже ничего не обнаружит и не вернет.

Цитата Сообщение от Stormhead Посмотреть сообщение
рекомендации по оформлению catch (Exception ex)
Цитата Сообщение от Stormhead Посмотреть сообщение
стоит ли перехватывать Exception вообще?
Зависит от контекста. Обычно, стОит перехватывать (обрабатывать) исключения, на которые нельзя повлиять прямо/косвенно. Например, при работе с файловой системой.
Если же в каком-то месте кода исключение ожидается и оно прогнозируемое, то лучше обойти его логическим путем, а не try/catch-ем.

Цитата Сообщение от Stormhead Посмотреть сообщение
Как лучше обрабатывать OperationCanceledException?
Возвращать не Errors.Common.OperationCancelled, а какой-л. Task Result.
0
 Аватар для Andrey-MSK
3360 / 2246 / 388
Регистрация: 14.08.2018
Сообщений: 7,589
Записей в блоге: 4
24.02.2026, 12:36
Stormhead, Вообще-то всё управляется через VM, написанными в этом слое командами. В пакете CommunityToolkit есть класс команды - AsyncRelayCommand(). И именно этими командами можно всё настроить - Ожидание, Отмену и т.д.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
24.02.2026, 18:23
Цитата Сообщение от Stormhead Посмотреть сообщение
но тогда в вызывающем коде приходится каждый раз проверять, не является ли ошибка отменой, чтобы скрыть уведомление.
Является ли ограничение единственным вызовом - ограничением Бизнес Логики или это ограничение, которое вы хотите установить в GUI?

По вашему коду, больше похоже на второй случай. И ExecuteAsync - это метод ViewModel, в таком случае.
Но зачем он тогда возвращает Таск? Для команд нужен void метод.

В общем случае (для Форм, WPF, UWP и др. GUI платформ), иерархия async методов строится так:
- на нижних уровнях Task методы. В них обрабатываются только исключения, которые нужны на их уровне. Например, при ошибке доступа к порту надо повторить его 10 раз. Все остальные исключения обрабатываться не должны или должны перевыкидываться. Например, после 10-го исключения доступа к порту, выкидвается исключение "10 ошибок соединения"
Task методы вкладываются друг в друга через await и при исключении на любом уровне оно автоматически прокидывается на самый верхний уровень.
- на верхнем уровне находится уже void метод, который вызывается из синхронного потока (главного потока приложения). В нём может происходить обработка исключения, чтобы при ожидаемых исключениях, приложение не падало. Не обрабатываются только исключения критические для приложения, при выкидывании которых, приложение обязано экстренно прекратить работу.
Обработка исключений может быть индивидуальной в каждом void методе. Но при большом количестве таких методов и более менее стандартизированной обработке, можно их делать централизовано, в каком-то общем классе или методе. Так же исключения централизовано можно обрабатывать в Application.DispatcherUnhandledException . Сюда попадают все исключения из пула Диспетчера. Верхние void методы в WPF выполняются в пуле Диспетчера, поэтому если их необрабатывать они все попадут в это событие. При какой-то похожести обработки в DispatcherUnhandledException и централизованной в void методе, между ними есть концептуальная разница. void Метод - это уровень ViewModel. DispatcherUnhandledException - это уровень приложения, который даже не входит не в один слоёв паттерна MVVM. Он лежит на всеми слоями и "знает обо всём".
1
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3439 / 2759 / 575
Регистрация: 04.09.2018
Сообщений: 8,660
Записей в блоге: 3
24.02.2026, 19:01
Тут, на самом деле, не совсем ясно в каком ключе автор задал вопрос. Привязки именно к MVVM-паттерну в вопросе не прослеживается.
Приведенный ТС-ом код точно не для размещения в VM: vm лишь, по какой-то команде, вызовет его из модели (не важно какой). А вот уже модель, на основании возвращенного результата отработавшего метода, уже создаст событие, на которое должна реагировать VM, со всеми своими "примочками", типа сообщений в окне или мессадж-боксах.
Глобальный перехват "необслуживаемых" исключений можно делать в App, так же, отдельным классом, который там инициализируется. Но такой подход предполагает наличие таких исключений из "ни от куда", которые явно нельзя предусмотреть заранее. И не является хорошей практикой..
2
1 / 1 / 1
Регистрация: 07.03.2018
Сообщений: 98
26.02.2026, 17:45  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это в Model или в ViewModel ?
Изначально данный функционал пытался реализовать в классе с именем BaseViewModel под свойство IsBusy для Loader'а в View. Сейчас понимаю, что не каждому VM необходим данный функционал, и исходя из сообщения wizard41, вовсе не должен присутствовать в VM.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А что вы хотите получить?
Хороший вопрос, который заставил меня "выпасть". Так как пишу проект как самоучка без конкретного ТЗ, как можете видеть из выше представленного названия, изначально хотел супер-обобщёный базовый класс.
Цитата Сообщение от wizard41 Посмотреть сообщение
Возвращать не Errors.Common.OperationCancelled, а какой-л. Task Result.
Проясню. Я использую неявное преобразование типа, ввиду чего этот error будет преобразован в Result.
C#
1
public static Result<TValue> Failure<TValue>(Error error) => Result<TValue>.Failure(error);
Andrey-MSK, я использую Prism. ICommand -> AsyncDelegateCommand -> ExecuteAsync.

Есть над чем подумать, но пока что откажусь от этой идеи и вернусь к ней в конкретный момент нужды.
Спасибо за все сообщения. Если вам есть что ещё сказать - всегда готов выслушать.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
26.02.2026, 20:32
Цитата Сообщение от Stormhead Посмотреть сообщение
Изначально данный функционал пытался реализовать в классе с именем BaseViewModel под свойство IsBusy
Давайте мыслить "от Модели".

Что такое для неё IsBusy?

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

А может быть такое, что устройство выполнять может несколько команд одновременно, но некоторые из них она повторно принять не может пока не завершит выполнение предыдущей такой же? Да, может. И в этом случае IsBusy это уже состояние не устройства в целом, а только состояние по отношению к определённому методу.

А как часто мы должны проверять IsBusy? Логично, если устройство нам сообщает об изменении своего состояния и по нему перепроверяем IsBusy.

Вот обобщением всего этого и является команда - интерфейс ICommand.
ICommand.Execute - запускает метод (команду устройства) Модели.
ICommand.CanExecute - проверят IsBusy Модели и определённого Метода.
ICommand.CanExecuteChanged - служит для извещения о необходимости перепроверки ICommand.CanExecute и, следовательно, перепроверки IsBusy Модели.

Имеет ли смысл вешать IsBusy на ViewModel в целом?
В общем случае нет, поскольку IsBusy на модели (устройстве) хоть и частый сценарий, но не относится к большей части. Поэтому ICommand.CanExecute хватает для почти всех сценариев.

Подходим к следующей итерации усложнения - Асинхронным командам.
Асинхронная команда - это всего лишь Task обертка:
C#
1
2
3
4
5
6
        // Таск обёртка
        public override async void Execute(object? parameter) => await Task.Factory.StartNew(base.Execute, parameter);
 
        protected RelayCommandAsync(Action<object?> execute)
            : base(execute)
        { }
Как следует поступать с исключениями в Асинхронных командах? Да, никак! Они автоматически прокидывают исключения в поток вызывающий команду. Поэтому обработка ошибок - это ответственность метода переданного через делегат execute. Нужна она там или нет - "личное дело" метода.
На практике, это очень зависит от конкретного задания.
В целом принято различать ошибки и критические исключения.
Ошибки - это исключения при которых можно продолжить работу приложения. Например, повторить запрос. Или сообщить пользователю, что неверный адрес.
Критические исключения - их тоже обрабатываю, но обычно это просто вывод сообщения пользователю и/или запись лога перед прекращением работы приложения.

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

Например, можно прокидывать все необработанные исключения в поток диспетчера. А на Application.DispatcherUnhandledException повесить запись лога, вывод окошка с сообщением и Shutdown приложения.

Так же может быть в каких-то случаях и какая-то типизированная обработка всех исключений в конкретном приложении. Но этот функционал точно не относится к обобщённой ViewModel.
3
 Аватар для Andrey-MSK
3360 / 2246 / 388
Регистрация: 14.08.2018
Сообщений: 7,589
Записей в блоге: 4
27.02.2026, 12:50
Цитата Сообщение от Stormhead Посмотреть сообщение
ICommand -> AsyncDelegateCommand -> ExecuteAsync
Ну значит там должно быть встроенное свойство команды - IsBusy. Когда команда вызывается, UI автоматом должен отключить свои элементы, которые привязаны к этой команде. По крайней мере в CommunityToolkit именно так и происходит, элемент не активен пока команда не отработала. А кнопка отмены привязывается на токен этой команды и все дела.
0
1 / 1 / 1
Регистрация: 07.03.2018
Сообщений: 98
27.02.2026, 19:13  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
должно быть встроенное свойство
Это я знаю и использую. Первой из идей, зачем мне нужен IsBusy в VM - это Loader, блокирующий форму, пока загружаются данные.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
27.02.2026, 19:25
Цитата Сообщение от Stormhead Посмотреть сообщение
Первой из идей, зачем мне нужен IsBusy в VM - это Loader, блокирующий форму, пока загружаются данные.
Ну, здесь нужна не просто блокировка команды, а блокировка всего GUI (или какого-то его региона).
Один из вариантов, это полупрозрачный фон в контроле с прогрессбаром в AdornerLayer и перекрывающий весь GUI.
Его видимость привязана к IsBusy в VM.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
27.02.2026, 19:25
Помогаю со студенческими работами здесь

Как отменить изменения в модели?
Есть приложение, в нем реализованы настройки через {Binding Settings.Sedault.setting_property_X},...

Отмена сворачивания окна
Никак не могу найти способа избежать сворачивания/скрытия окна при нажатии Win+D (&quot;Скрыть все...

WebBrowser и MVVM отловить и отменить событие клика
Есть компонент &lt;WebBrowser behaviors:WebBrowserBehavior.Html=&quot;{Binding Document}&quot; /&gt;...

Отмена действий горячих клавиш
Здравствуйте. Есть textBox, в котором я прописываю комбинации клавиш. Но если я, например, нажму...

Отменить прокрутку ScrollViewer
Доброго времени суток. Есть проблемка. Я сделал, что при вращении колесика над Image, если зажат...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
модель ЗдравоСохранения 8. Подготовка к разному выполнению заданий
anaschu 08.04.2026
https:/ / github. com/ shumilovas/ med2. git main ветка * содержимое блока дэлэй из старой модели теперь внутри зайца новой модели 8ATzM_2aurI
Блокировка документа от изменений, если он открыт у другого пользователя
Maks 08.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа, разработанного в конфигурации КА2. Задача: запретить редактирование документа, если он открыт у другого пользователя. / / . . .
Система безопасности+живучести для сервера-слоя интернета (сети). Двойная привязка.
Hrethgir 08.04.2026
Далее были размышления о системе безопасности. Сообщения с наклонным текстом - мои. А как нам будет можно проверить, что ссылка наша, а не подделана хулиганами, которая выбросит на другую ветку и. . .
Модель ЗдрввоСохранения 7: больше работников, больше ресурсов.
anaschu 08.04.2026
работников и заданий может быть сколько угодно, но настроено всё так, что используется пока что только 20% kYBz3eJf3jQ
Дальние перспективы сервера - слоя сети с космологическим дизайном интефейса карты и логики.
Hrethgir 07.04.2026
Дальнейшее ближайшее планирование вывело к размышлениям над дальними перспективами. И вот тут может быть даже будут нужны оценки специалистов, так как в дальних перспективах всё может очень сильно. . .
Горе от ума
kumehtar 07.04.2026
Эта мне ментальная установка, что вот прямо сейчас, мол, мне для полного счастья не хватает (нужное вписать), и когда я этого достигну - тогда и полный кайф. Одна из самых сильных ловушек на пути. . . .
Использование значений реквизитов справочника в документе, с определенными условиями и правами
Maks 07.04.2026
1. Контроль срока действия договора Алгоритм из решения ниже реализован на примере нетипового документа "ЗаявкаНаРаботу", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если. . .
Доступность команды формы по условию
Maks 07.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: сделать доступной кнопку (команда формы "ЗавершитьСписание") при. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru