【问题标题】:Catch exceptions in async loading of dialog viewmodel在对话框视图模型的异步加载中捕获异常
【发布时间】:2014-12-26 16:20:19
【问题描述】:

我有一个带有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()。这样用户必须等待数据加载才能真正看到窗口,但我希望窗口立即显示加载指示器。

【问题讨论】:

  • 不幸的是,这个问题的答案不适合我。我已经写过Show()。我想需要从外部调用“LoadData()”,因为只有调用者才能确定是否加载数据。至于'BackgroundWorker' - 我希望有一种方法可以使用异步等待......
  • WindowManager.ShowDialog() 来自哪里?是 Caliburn Micro 吗?
  • 是的,但这没关系:它可以是任何ShowDialog() 实现,例如查看注入。

标签: c# wpf mvvm dialog async-await


【解决方案1】:

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

如果必须发生这种情况,则在构造函数中异步执行此操作,使用 Task<T>ContinueWith 来处理通过检查返回任务上的 IsFaultedproperty 生成的任何异常。

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

一个非常简单的例子如下所示,显然你的实现会更复杂。

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&lt;T&gt; 并想使用 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"
        });
    }
}

【讨论】:

  • 但是LoadData() 将始终被调用,但我可能希望在不加载任何数据的情况下显示此对话框(如我在第一种情况中所述)。
  • 嗯,这就是我认为你有问题的地方,要么应该总是调用它,要么永远不应该调用它。因为否则何时应该调用它并不明显......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 2011-08-12
  • 2013-04-20
  • 1970-01-01
  • 1970-01-01
  • 2016-05-28
  • 1970-01-01
相关资源
最近更新 更多