I\'ve looked around at a number of the ReactiveUI samples, but I can\'t see a good simple example of how to handle exceptions, where a message should be displayed to the use
You're understanding ThrownExceptions
, but it's on the wrong guy, _theAnswer.ThrownExceptions
will receive the Exception. But the tricky part, is now that button doesn't work any more - once an Observable ends OnError, it's done for good.
You end up having to do a few backflips here, something like:
static IObservable<int?> AnswerCalculator()
CalculateTheAnswer
.SelectMany(_ => AnswerCalculator())
.Catch(Observable.Return(null))
.Where(x => x != null)
.Select(x => x.Value)
.ToProperty(this, x => x.TheAnswer);
In this case, ReactiveAsyncCommand
is much easier, since a new IObservable
is created for every invocation, so you'd do:
// ReactiveAsyncCommand handles exceptions thrown for you
CalculateTheAnswer.RegisterAsyncTask(_ => AnswerCalculator())
.ToProperty(this, x => x.TheAnswer);
CalculateTheAnswer.ThrownExceptions.Subscribe(ex => MessageBox.Show("Aieeeee"));
So, UserError
is like an exception intended to be thrown at a user (i.e. it contains friendly text, not programmer text)
To use UserError
, you have to do two things - first, change your ThrownExceptions:
CalculateTheAnswer.ThrownExceptions
.SelectMany(ex => UserError.Throw("Something bad happened", ex))
.Subscribe(result => /* Decide what to do here, either nothing or retry */);
And in your View code-behind, call `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);
});
The cool part, is that this makes error dialogs testable - in a unit test:
var fixture = new MainWindowViewModel();
bool errorCalled;
using (UserError.OverrideHandlersForTesting(_ => { errorCalled = true; return RecoveryOptionResult.CancelOperation })) {
CalculateTheAnswer.Execute(null);
}
Assert.True(errorCalled);