требуется ли асинхронная версия relaycommand для корректного запуска асинхронных методов
у меня есть следующий код, определенный в viewmodel. Я думаю, что SaveAsync типа Func<Task>
преобразуется в действие, так как RelayCommand принимает действие, а не Func<Task>
но я не совсем понимаю последствия этого.
1) Необходимо ли заменить RelayCommand асинхронной версией (RelayCommandAsync)? 2) что именно делает текущий код в отношении асинхронности? 3) что, если что-нибудь может / я должен изменить, чтобы улучшить / исправить это?
private ICommand _saveCommand;
public ICommand SaveCommand
{
get { return _saveCommand ?? (_saveCommand = new RelayCommand(async () => await SaveAsync(), CanSave)); }
}
public bool CanSave()
{
return !IsBusy;
}
private async Task SaveAsync()
{
IsBusy = true;
try
{
await _service.SaveAsync(SomeProperty);
}
catch ( ServiceException ex )
{
Message = "Oops. " + ex.ToString();
}
finally
{
IsBusy = false;
}
}
спасибо!
EDIT: после некоторых экспериментов кажется, что сам асинхронный метод работает так, как должен. И не имело значения, был ли асинхронный/await включен в лямбду, и был ли метод определен как async Task
или async void
.
однако то, что не работает правильно, это canExecute
функциональность предиката, которая автоматически включает / отключает привязку элемента управления к команде. Что происходит, заключается в том, что кнопка правильно отключена во время выполнения асинхронного метода, но после этого она не включена. Я должен щелкнуть где-нибудь в окне один раз, а затем он снова включается.
таким образом, асинхронная версия RelayCommand требуется для полной функциональности, т. е. so canExecute
может сделать свое дело правильно.
3 ответов
1) Необходимо ли заменить RelayCommand асинхронной версией (RelayCommandAsync)?
это не должно быть, но вы должны рассмотреть это.
2) что именно делает текущий код в отношении асинхронности?
он создает async void
лямда. Это проблематично, потому что async void
не обрабатывает исключения особенно хорошо. Если вы используете RelayCommand
с асинхронным кодом, тогда вы определенно хочу использовать try
/catch
как в коде.
3), что если что мог/я должен изменить, чтобы улучшить/исправить?
если это единственная асинхронная команда в вашем коде, я бы сказал, что все в порядке. Однако, если вы обнаружите, что у вас есть несколько асинхронных команд в вашем приложении с аналогичной семантикой, вам следует рассмотреть возможность написания RelayCommandAsync
.
нет стандартного шаблона (пока); я выделяю несколько разных подходит в статья MSDN. Лично я, по крайней мере, определяю IAsyncCommand
в моих приложениях, которые я выставляю из своих VMs (трудно тестировать асинхронный ICommand
).
однако неправильно работает функция предиката canExecute, которая автоматически включает / отключает привязку элемента управления к команде.
предполагая, что RelayCommand.CanExecuteChanged
- это передача CommandManager
, тогда вы можете просто позвонить CommandManager.InvalidateRequerySuggested
после настройка IsBusy
.
необходимо ли заменить RelayCommand асинхронной версией (RelayCommandAsync)?
нет, RelayCommand
будет работать по желанию здесь.
что именно делает текущий код в отношении асинхронности?
что происходит, когда разрешение перегрузки срабатывает во время компиляции, он выбирает перегрузку, которая принимает Action
, что означает, что ваш метод переведен в async void
и именно поэтому ваш код составляет.
3), что если что мог/я должен изменить, чтобы улучшить/исправить?
существуют реализации команд асинхронного делегата. Вы можете найти один здесь. Важно отметить, что обработка исключений. В случае исключения unhandeled внутри асинхронного ICommand
который привязан к элементу управления WPF, исключение будет предлагать связующему и идти необработанным и незамеченным.
Я не думаю, что вам нужна асинхронная версия команды relay. Ваша реализация выглядит нормально. Это работает?
Если вы хотите проверить, работает ли тело асинхронно, добавьте задачу await.задержка(20000) и посмотреть, если пользовательский интерфейс остается отзывчивым во время выполнения команды.