简体   繁体   English

执行返回ObservableCollection的异步搜索

[英]Perform async search that returns ObservableCollection

I'm using Visual Studio 2015 and Entity Framework 6 to build an MVVM Light WPF app. 我正在使用Visual Studio 2015和Entity Framework 6来构建MVVM Light WPF应用程序。 When the user clicks the Search button, it calls a RelayCommand which has been defined like this in the View Model's constructor: 当用户单击“搜索”按钮时,它会调用“ RelayCommand ,该命令已在“视图模型”的构造函数中定义如下:

SearchEmployeesRelayCommand = new RelayCommand(SearchEmployees);

The SearchEmployees method in the View Model looks like this: 视图模型中的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();
}

The data service's search method looks like this: 数据服务的搜索方法如下:

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);
    }
}

If I try to make the above method async and awaitable , with something like this: 如果我尝试使上述方法asyncawaitable ,如下所示:

return await new ObservableCollection<EmployeeViewModel>(query);

It gives this error: 它给出了这个错误:

'ObservableCollection' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accepting a first argument of type 'ObservableCollection' could be found (are you missing a using directive or an assembly reference?) 'ObservableCollection'不包含'GetAwaiter'的定义,并且找不到扩展方法'GetAwaiter'接受类型为'ObservableCollection'的第一个参数(您是否缺少using指令或程序集引用?)

How do you make the search async if it's returning an ObservableCollection ? 如果返回了ObservableCollection如何使搜索async Thanks. 谢谢。

Update : For the busy indicator to work, I had to make this change: 更新 :为了使繁忙的指示器正常工作,我必须进行以下更改:

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

And I removed the _worker.RunWorkerCompleted block altogether. 然后,我完全删除了_worker.RunWorkerCompleted块。 There probably is a better way to do that, but this was how I got it working. 可能有更好的方法来做到这一点,但这就是我如何使其工作的方式。

There are a couple of approaches. 有两种方法。 First, you can keep your database access synchronous and just run it on a background thread. 首先,您可以保持数据库访问同步,并仅在后台线程上运行它。 Note that Task.Run is a modern replacement for BackgroundWorker (I have a blog series that draws parallels between the two ): 请注意, Task.RunBackgroundWorker的现代替代品(我有一个博客系列在两者之间进行了比较 ):

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;
  }
}

Alternatively, since you are using EF6, you can make your database query asynchronous and not mess around with background threads at all: 另外,由于使用的是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;
  }
}

You should not make BackgroundWorker.DoWork asynchronous; 应该让BackgroundWorker.DoWork异步的; that will cause it to "end early" and prevent it from gracefully handling exceptions. 这将导致它“提早结束”并阻止其正常处理异常。 BGW simply wasn't designed to work with async code. BGW并非旨在与async代码一起使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM