在对话框视图模型的异步加载中捕获异常

本文关键字:加载 捕获异常 异步 对话框 视图 模型 | 更新日期: 2023-09-27 18:28:31

我有一个带有async Task LoadData()方法的DialogViewModel类。此方法异步加载数据,并显示此对话框,通知用户有关加载的信息。这是代码:

try
{
    var dialog = new DialogViewModel();
    var loadTask = dialog.LoadData();
    WindowManager.ShowDialog(dialog);
    await loadTask;
}
catch (Exception ex)
{
    Logger.Error("Error in DialogViewModel", ex);
    // Notify user about the error
}

LoadData抛出异常时,直到用户退出对话框时才会进行处理。之所以会发生这种情况,是因为在调用await时会处理异常,而在WindowManager.ShowDialog(dialog)完成之前不会发生这种情况。

使用异步加载显示对话框的正确方式是什么?我试过这种方法:

  1. OnShow()、构造函数或类似程序中调用LoadData()。但如果我需要在没有任何数据的情况下显示此对话框,这将不起作用
  2. 显示对话框之前,请致电await LoadData()。通过这种方式,用户必须等待数据加载后才能真正看到窗口,但我希望窗口能立即显示加载指示符

在对话框视图模型的异步加载中捕获异常

为什么有一个显式的公共LoadData方法?

如果必须发生这种情况,则在构造函数内部使用Task<T>ContinueWith异步执行,以处理通过检查返回任务的IsFaulted属性生成的任何异常。

这将解决您强调的两个问题。

下面显示了一个非常简单的示例,但您的实现会更加复杂。

public class DialogViewModel
{
    private Task _task;
    public DialogViewModel()
    {
        var context = TaskScheduler.FromCurrentSynchronizationContext();
        _task = Task.Factory.StartNew(() =>
            {
                var data = GetDataCollection();
                return data;
            })
            .ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    HasErrored = true;
                    ErrorMessage = "It's borked!";
                }
                else
                {
                    Data = t.Result;
                }
            }, context);
    }
    public IEnumerable<string> Data { get; private set; }
    public bool HasErrored { get; private set; }
    public string ErrorMessage { get; private set; }
    private static IEnumerable<string> GetDataCollection()
    {
        return new List<string>()
        {
            "John",
            "Jack",
            "Steve"
        };
    }
}

或者,如果您不想显式使用Task<T>,而想使用async''await功能,您可以使用稍微不同的方法,因为您不能将async''await与类构造函数一起使用:

public class DialogViewModel
{
    public IEnumerable<string> Data { get; private set; }
    public bool HasErrored { get; private set; }
    public string ErrorMessage { get; private set; }
    async public static Task<DialogViewModel> BuildViewModelAsync()
    {
        try
        {
            var data = await GetDataCollection();
            return new DialogViewModel(data);
        }
        catch (Exception)
        {
            return new DialogViewModel("Failed!");
        }
    }
    private DialogViewModel(IEnumerable<string> data)
    {
        Data = data;
    }
    private DialogViewModel(string errorMessage)
    {
        HasErrored = true;
        ErrorMessage = errorMessage;
    }
    private async static Task<IEnumerable<string>> GetDataCollection()
    {
        // do something async...
        return await Task.Factory.StartNew(() => new List<string>()
        {
            "John",
            "Jack",
            "Steve"
        });
    }
}