Обработка исключений 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, потому что это наблюдаемое не получение любых элементов при запуске кода выше. Что я делаю не так?
мой второй вопрос заключается в том, как бы я сделал это MVVM-дружественным способом. эта запись в блоге упоминает Ошибки Пользователей функция, но я не могу найти никакой документации о том, как ее использовать. Как бы я реализовал его в приведенном выше примере?
редактировать: я опубликовал пример решения на github на основе ответа Павла ниже.
1 ответов
ты понимаешь ThrownExceptions
, но он не на того парня, _theAnswer.ThrownExceptions
получите исключение. Но самое сложное, что теперь эта кнопка больше не работает - как только наблюдаемый заканчивается, это делается навсегда.
вам в конечном итоге придется сделать несколько сальто назад здесь, что-то вроде:
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 */);
и в вашем представлении код-за, вызовите "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);