在 UWP 中使用 Caliburn.Micro 从构造函数调用异步方法

Async method call from constructor using Caliburn.Micro in UWP

我有以下 ViewModel 结构:

我通过以下代码在 MainViewModel 和 DetailViewModel 之间导航:

navigationService.For<DetailViewModel>().Navigate();

是DetailViewModel的构造函数:

public DetailViewModel(INavigationService navigationService)
{
  GetData();
}

private async Task GetData()
{
  using (var context = new MyDataContext())
  {
    var result = await (from data in context.Data
                          select data).ToListAsync();
    DataList = new ObservableCollection<Data>(result);
  }
}

因为我在调用 GetData 时不使用 await,所以构造函数应该 return 非常快并且稍后应该填充列表。

我遇到非常慢的导航,我单击 MainViewModel 中的项目,GUI 冻结了一秒钟,我看到详细信息 列表已填充(调试显示构造函数在列表完成之前完成)。

我看到一条警告说:

Because this call is not awaited, execution of the current method continues before the call is completed.

那是什么阻碍了?我还应该设置什么?

用这个替换您的代码:

public DetailViewModel(INavigationService navigationService)
{
    Task.Run(() =>
        {
            GetData();
        });
}

private void GetData()
{
    using (var context = new MyDataContext())
    {
        var result = await (from data in context.Data
                            select data).ToListAsync();

        DataList = new ObservableCollection<Data>(result);
    }
}

按预期方式工作:

public DetailViewModel(INavigationService navigationService)
{
    Task.Run(() =>
        {
            GetData();
        }).ContinueWith(
      (x) =>
      {
        Execute.OnUIThread(
              () =>  DataList = new ObservableCollection<Data>(x.Result));
      });
    };
}

private Task<List<Data>> GetData()
{
    using (var context = new MyDataContext())
    {
        return await (from data in context.Data
                            select data).ToListAsync();

    }
}

感谢您的建议。

AFAIK,SQLite Entity Framework 提供程序不支持真正的异步方法。所以,对 ToListAsync 的调用实际上是同步的,导致你的延迟。

解决此问题的最佳方法是将 Task.Runawait 一起使用( 而不是 ContinueWithExecute.OnUIThread):

public DetailViewModel(INavigationService navigationService)
{
  InitializeAsync();
}

private async Task InitializeAsync()
{
  try
  {
    DataList = await Task.Run(() => GetData());
  }
  catch
  {
    // TODO: Log
  }
}

private List<Data> GetData()
{
  using (var context = new MyDataContext())
  {
    return context.Data.ToList();
  }
}

注意引入一个try/catch;此处的错误处理是必要的,因为 InitializeAsync 被视为 "top-level" 异步操作。