【问题标题】:Perform async search that returns ObservableCollection执行返回 ObservableCollection 的异步搜索
【发布时间】:2016-07-25 16:45:24
【问题描述】:

我正在使用 Visual Studio 2015 和 Entity Framework 6 构建一个 MVVM Light WPF 应用程序。当用户点击 Search 按钮时,它会调用一个 RelayCommand,它在 View Model 的构造函数中是这样定义的:

SearchEmployeesRelayCommand = new RelayCommand(SearchEmployees);

View Model 中的SearchEmployees 方法如下所示:

private BackgroundWorker _worker;

public void SearchEmployees()
{
    _worker = new BackgroundWorker(); // use this to show busy indicator

    var dataService = new EmployeeDataService();
    _worker.DoWork += (o, ea) =>
    {
        SearchResults = dataService.SearchEmployees(SelectedColumn, SearchValue);
    };
    _worker.RunWorkerCompleted += (o, ea) =>
    {
        IsSearching = false;
    };

    IsSearching = true;
    _worker.RunWorkerAsync();
}

数据服务的搜索方法如下:

public ObservableCollection<EmployeeViewModel> 
    SearchEmployees(string selectedColumn, string searchValue)
{
    var paramEmployee = Expression.Parameter(typeof(Employee), "e");

    var comparison = Expression.Lambda<Func<Employee, bool>>(
        Expression.Equal(
            Expression.Property(paramEmployee, selectedColumn),
            Expression.Constant(searchValue)),
            paramEmployee).Compile();

    using (var context = new MyEntities())
    {
        var query = (from e in context.Employees
                     .Where(comparison)
                     select new EmployeeViewModel
                     {
                         // Various EF model properties...
                     });
        return new ObservableCollection<EmployeeViewModel>(query);
    }
}

如果我尝试使用上述方法asyncawaitable,则如下:

return await new ObservableCollection<EmployeeViewModel>(query);

它给出了这个错误:

“ObservableCollection”不包含“GetAwaiter”的定义,并且找不到接受“ObservableCollection”类型的第一个参数的扩展方法“GetAwaiter”(您是否缺少 using 指令或程序集引用?)

如果搜索async 返回ObservableCollection,您如何进行搜索?谢谢。

更新:为了让忙碌指示器工作,我必须做出这样的改变:

_worker.DoWork += async (o, ea) =>
{
    SearchResults = await dataService
        .SearchEmployees(selectedColumnValue, SearchValue);
    IsSearching = false;
};

我完全删除了_worker.RunWorkerCompleted 块。可能有更好的方法来做到这一点,但这就是我的工作方式。

【问题讨论】:

标签: c# wpf asynchronous mvvm async-await


【解决方案1】:

有几种方法。首先,您可以保持数据库访问同步并在后台线程上运行它。请注意Task.RunBackgroundWorker 的现代替代品(我有一个blog series that draws parallels between the two):

public async Task SearchEmployeesAsync()
{
  var dataService = new EmployeeDataService();
  var selectedColumn = SelectedColumn;
  var searchValue = searchValue;

  IsSearching = true;
  try
  {
    SearchResults = await Task.Run(() => dataService.SearchEmployees(selectedColumn, searchValue));
  }
  finally
  {
    IsSearching = false;
  }
}

另外,由于您使用的是 EF6,因此您可以使您的数据库查询异步,而完全不会与后台线程发生冲突:

public async Task<ObservableCollection<EmployeeViewModel>> 
    SearchEmployeesAsync(string selectedColumn, string searchValue)
{
  var paramEmployee = Expression.Parameter(typeof(Employee), "e");
  var comparison = Expression.Lambda<Func<Employee, bool>>(
    Expression.Equal(
      Expression.Property(paramEmployee, selectedColumn),
      Expression.Constant(searchValue)),
      paramEmployee).Compile();

  using (var context = new MyEntities())
  {
    var query = (from e in context.Employees
                 .Where(comparison)
                 select new EmployeeViewModel
                 {
                     // Various EF model properties...
                 });
    var data = await query.ToListAsync();
    return new ObservableCollection<EmployeeViewModel>(data);
  }
}

public async Task SearchEmployeesAsync()
{
  var dataService = new EmployeeDataService();
  IsSearching = true;
  try
  {
    SearchResults = await dataService.SearchEmployeesAsync(SelectedColumn, SearchValue);
  }
  finally
  {
    IsSearching = false;
  }
}

你应该使BackgroundWorker.DoWork异步;这将导致它“提前结束”并阻止它优雅地处理异常。 BGW 根本不是为使用 async 代码而设计的。

【讨论】:

  • 谢谢,@StephenCleary。 query.ToListAsync() 是否有原因,然后从中获得新的 ObservableCollection
  • RelayCommand 给我带来了麻烦,这是一个没有返回类型的委托方法名称。如何在这一行 SearchEmployeesRelayCommand = new RelayCommand(SearchEmployees); 上将其返回类型从 void 更改为 Task
  • 我认为这解决了RelayCommand返回值的问题:stackoverflow.com/questions/2240042/…
  • @Alex:回复ToListAsync,只是为了更清晰而拆分。重新异步ICommands,我写了an article on the subject。如果你想快速修复,你可以使用async void,但要注意你应该在整个方法体周围有一个try/catch
猜你喜欢
  • 2022-01-11
  • 2021-07-16
  • 1970-01-01
  • 2010-10-28
  • 2010-10-11
  • 1970-01-01
  • 2022-01-24
  • 2015-06-05
相关资源
最近更新 更多