ReactiveUI异常处理

时间:2012-12-19 09:57:17

标签: c# reactiveui

我已经查看了一些ReactiveUI示例,但是我看不到一个如何处理异常的简单示例,其中应该向用户显示消息。 (如果有一个很好的例子,有人能指出我吗?)。

我的第一个问题是如何使用ReactiveCommand和ToProperty处理异常。例如,我有以下代码:

public class MainWindowViewModel : ReactiveObject
{
    public ReactiveCommand CalculateTheAnswer { get; set; }

    public MainWindowViewModel()
    {
        CalculateTheAnswer = new ReactiveCommand();

        CalculateTheAnswer
            .SelectMany(_ => AnswerCalculator())
            .ToProperty(this, x => x.TheAnswer);

        CalculateTheAnswer.ThrownExceptions
            .Select(exception => MessageBox.Show(exception.Message));
    }

    private readonly ObservableAsPropertyHelper<int> _theAnswer;
    public int TheAnswer
    {
        get { return _theAnswer.Value; }
    }

    private static IObservable<int> AnswerCalculator()
    {
        var task = Task.Factory.StartNew(() =>
        {
            throw new ApplicationException("Unable to calculate answer, because I don't know what the question is");
            return 42;
        });

        return task.ToObservable();
    }
}

我认为我必须误解ThrownExceptions,因为当我运行上面的代码时,这个observable没有收到任何项目。我做错了什么?

我的第二个问题是我将如何以MVVM友好的方式执行此操作。 This blog entry提及用户错误功能,但我找不到有关如何使用它的任何文档。我将如何在上面的例子中实现它?

修改:我已根据Paul的回答发布了一个示例解决方案on github

1 个答案:

答案 0 :(得分:25)

你理解ThrownExceptions,但它是错误的人,_theAnswer.ThrownExceptions会收到异常。但棘手的部分是,现在按钮不再起作用了 - 一旦Observable结束OnError,它就完成了。

你最终不得不在这里做一些后空翻,比如:

static IObservable<int?> AnswerCalculator()

CalculateTheAnswer
    .SelectMany(_ => AnswerCalculator())
    .Catch(Observable.Return(null))
    .Where(x => x != null)
    .Select(x => x.Value)
    .ToProperty(this, x => x.TheAnswer);

在这种情况下,ReactiveAsyncCommand要容易得多,因为每次调用都会创建一个新的IObservable,所以你可以这样做:

// ReactiveAsyncCommand handles exceptions thrown for you
CalculateTheAnswer.RegisterAsyncTask(_ => AnswerCalculator())
    .ToProperty(this, x => x.TheAnswer);

CalculateTheAnswer.ThrownExceptions.Subscribe(ex => MessageBox.Show("Aieeeee"));

如何使用UserError

因此,UserError就像是一个旨在抛出用户的异常(即它包含友好的文本,而不是程序员文本)

要使用UserError,您必须做两件事 - 首先,更改您的ThrownExceptions:

CalculateTheAnswer.ThrownExceptions
    .SelectMany(ex => UserError.Throw("Something bad happened", ex))
    .Subscribe(result => /* Decide what to do here, either nothing or retry */);

在您的View代码隐藏中,调用`RegisterHandler“:

UserError.RegisterHandler(err => {
    MessageBox.Show(err.ErrorMessage);

    // This is what the ViewModel should do in response to the user's decision
    return Observable.Return(RecoveryOptionResult.CancelOperation);
});

很酷的部分是,这使得错误对话框可以测试 - 在单元测试中:

var fixture = new MainWindowViewModel();
bool errorCalled;

using (UserError.OverrideHandlersForTesting(_ => { errorCalled = true; return RecoveryOptionResult.CancelOperation })) { 
    CalculateTheAnswer.Execute(null);
}

Assert.True(errorCalled);