【问题标题】:Running an async method inside a relaycommand using MVVM使用 MVVM 在中继命令中运行异步方法
【发布时间】:2015-10-13 11:55:56
【问题描述】:

我正在使用 WPF 和 MVVM 开发一个异步应用程序,但我似乎无法在我的中继命令中运行异步方法。

我的 WPF view 上有一个按钮连接到我的 viewmodel 中的中继命令,它尝试在我的 model 中调用异步方法> 返回结果列表:

/// <summary>
/// Search results 
/// </summary>
private ObservableCollection<string> _searchResults = new ObservableCollection<string>(); 
public IList<string> SearchResults
{
    get { return _searchResults; }
}

/// <summary>
/// Search button command
/// </summary>
private ICommand _searchCommand;
public ICommand SearchCommand
{
    get
    {
        _searchCommand = new RelayCommand(
            async() =>
            {
                SearchResults.Clear();
                var results = await DockFindModel.SearchAsync(_selectedSearchableLayer, _searchString);
                foreach (var item in results)
                {
                    SearchResults.Add(item);
                }                    
                //notify results have changed
                NotifyPropertyChanged(() => SearchResults);
            },
            () => bAppRunning); //command will only execute if app is running
        return _searchCommand;
    }
}

但是,当中继命令尝试执行时,我得到以下异常:

在 mscorlib.dll 中发生“System.AggregateException”类型的未处理异常

附加信息:也未观察到任务的异常 通过等待任务或访问其异常属性。作为一个 结果,未观察到的异常被终结器线程重新抛出。

我在this thread 中尝试了很多方法来尝试解决问题,但没有成功。有谁知道如何解决这个问题?

【问题讨论】:

  • 内部异常是什么?
  • 你的 RelayCommand 是什么样的?
  • 修复了!无论如何,谢谢各位。
  • 修好了还是躲过了?
  • 我知道我在这里唤醒了一个旧线程,但您不应该等待 lambda 函数吗?我会尝试将async() =&gt; 更改为async() =&gt; await

标签: c# wpf mvvm async-await


【解决方案1】:

好的,所以我实际上设法通过更改运行异步方法的方式来解决问题。

我改变了这个:

var results = await DockFindModel.SearchAsync();

到这里:

var results = await QueuedTask.Run(() => DockFindModel.SearchAsync());

虽然我对为什么在 relaycommand 已经接受异步 lambda 时需要等待 Task.Run() 感到有些困惑。不过,我相信随着时间的推移它会变得清晰。

感谢评论的人。

【讨论】:

    【解决方案2】:

    不确定您的 RelayCommand 来自何处(MVVM 框架或自定义实现),但请考虑使用异步版本。

    public class AsyncRelayCommand : ICommand
    {
        private readonly Func<object, Task> execute;
        private readonly Func<object, bool> canExecute;
    
        private long isExecuting;
    
        public AsyncRelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute ?? (o => true);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        public void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    
        public bool CanExecute(object parameter)
        {
            if (Interlocked.Read(ref isExecuting) != 0)
                return false;
    
            return canExecute(parameter);
        }
    
        public async void Execute(object parameter)
        {
            Interlocked.Exchange(ref isExecuting, 1);
            RaiseCanExecuteChanged();
    
            try
            {
                await execute(parameter);
            }
            finally
            {
                Interlocked.Exchange(ref isExecuting, 0);
                RaiseCanExecuteChanged();
            }
        }
    }
    

    【讨论】:

    • 如果execute 中有异常,Visual Studio 调试器将突出显示await execute(parameter); 行。我期待调试器突出显示 execute 委托中的实际错误行。我们能以某种方式解决这个问题吗?
    【解决方案3】:

    RelayCommand 类可能正在接受Action 类型的参数。由于您传递的是异步 lambda,因此我们在这里有一个 async void 场景。 您的代码将被解雇并忘记。考虑使用将Func&lt;Task&gt; 作为参数而不是Action 的ICommand 实现。

    【讨论】:

      猜你喜欢
      • 2015-02-26
      • 2015-08-24
      • 2021-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多