требуется ли асинхронная версия 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) и посмотреть, если пользовательский интерфейс остается отзывчивым во время выполнения команды.