是正确运行异步方法所需的relaycommand的异步版本

时间:2014-08-02 20:52:51

标签: c# .net asynchronous async-await relaycommand

我在viewmodel中定义了以下代码。我认为类型Func<Task>的SaveAsync正在转换为Action,因为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;
    }
}

谢谢!

编辑:经过一些实验后,似乎异步方法本身就能正常工作。是否包含在lambda中的async / await以及该方法是否定义为async Taskasync void并没有什么区别。

但是,无法正常工作的是canExecute谓词功能,可自动启用/禁用对命令的控件绑定。发生的情况是,在异步方法运行时正确禁用了该按钮,但之后未启用该按钮。我必须在窗口上的某个位置单击一次然后再次启用它。

因此,完整功能似乎需要一个异步版本的RelayCommand,即canExecute可以正确地完成它。

4 个答案:

答案 0 :(得分:19)

  

1)是否需要用异步版本(RelayCommandAsync)替换RelayCommand?

它不是必须的,但你应该考虑它。

  

2)目前的代码究竟与异步有什么关系?

它正在创建一个async void lambda。这是有问题的,因为async void不能特别好地处理异常。如果您对{1}}使用异步代码,那么您肯定希望使用代码中的RelayCommand / try

  

3)如果有什么可以/我应该改变以改进/纠正它怎么办?

如果这是您代码中唯一的异步命令,我说它没问题。但是,如果您发现应用程序中有多个异步命令具有相似的语义,那么您应该考虑编写catch

尚无标准模式;我在MSDN article中概述了一些不同的方法。就个人而言,我至少在我的应用程序中定义了一个RelayCommandAsync,我从我的虚拟机中公开了它(很难对异步IAsyncCommand进行单元测试。)

  

但是,无法正常工作的是canExecute谓词功能,它自动启用/禁用对命令的控件绑定。

假设ICommand委托给RelayCommand.CanExecuteChanged,那么您可以在设置CommandManager后致电CommandManager.InvalidateRequerySuggested

答案 1 :(得分:4)

  

是否需要使用异步版本(RelayCommandAsync)替换RelayCommand?

不,RelayCommand将在此处按预期工作。

  

目前的代码究竟与异步有什么关系?

当重载解析在编译时启动时会发生什么事情,它会选择需要Action的重载,这意味着您的方法被转换为async void,这就是您的代码编译的原因。< / p>

  

3)如果有什么可以/我应该改变以改进/纠正它怎么办?

有异步委托命令的实现。你可以找到一个here。需要注意的一件重要事情是异常处理。如果在绑定到WPF控件的异步ICommand中存在未经处理的异常,则该异常将传播到绑定器并且无法取消并且不被注意。

答案 2 :(得分:2)

我认为您不需要异步版本的relay命令。您的实现看起来不错。它有用吗?

如果要测试正文是否正在运行异步,请添加await task.delay(20000),并在命令运行时查看UI是否保持响应。

答案 3 :(得分:1)

不。在绑定上设置IsAsync。像这样:

<Button Command="{Binding DoCommand, IsAsync=True}" />