Глобальная обработка исключений в MVVM

#c# #wpf #mvvm

#c# #wpf #mvvm

Вопрос:

Есть ли способ реализовать глобальную обработку исключений с помощью шаблона MVVM. В моем существующем случае всякий раз, когда внутри ViewModel возникает ошибка, приложение не аварийно завершает работу, а просто «скрывает» остальные привязки, которые происходят после кода, вызвавшего ошибку (конечно, это очень вводит в заблуждение конечного пользователя, а не соответствует действительности, и никогда не должно происходить таким образом). Я бы не хотел реализовывать try catch для каждой операции в ViewModel, и мне не нравится тихий способ исключения ошибок, я бы очень хотел реализовать способ для приложения WPF обрабатывать глобальные ошибки. Есть ли способ сделать это с помощью MVVM?

Комментарии:

1. Когда вы говорите «ошибка происходит внутри ViewModel», какая именно ошибка это? Или вы имеете в виду ошибки привязки ( System.Windows.Data ошибки)? Откуда вы знаете, что ошибка вообще происходит?

2. Вы имеете в виду, как msdn.microsoft.com/en-us/library /… ?

3. Ну, чтобы упростить ответ на ваш вопрос, в целях тестирования я вызываю throw new Exception(); в середине моей операции привязки ViewModel

4. Обработка глобального исключения с помощью AppDomain. Необработанное исключение работает для всего остального, кроме ошибок, возникающих внутри привязки ViewModel

5. Когда вы говорите «в середине моей операции привязки ViewModel», вы имеете в виду, что генерируется средство получения или установки свойства? Если бы вы могли опубликовать какой-нибудь фактический код, это могло бы помочь нам помочь вам.

Ответ №1:

После долгой битвы, наконец, я нашел очень простой способ реализовать обработку исключений внутри ViewModel. Хотя создание BindingListener, который наследуется от DefaultTraceListener, безусловно, является отличным способом поиска ошибок привязки в режиме отладки, это не приведет к перехвату исключений, возникших внутри ViewModel при запуске solution в стандартном режиме. Но AppDomain.CurrentDomain.Исключение FirstChanceException будет.

App.xaml.cs:

 AppDomain.CurrentDomain.FirstChanceException  = new EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>(CurrentDomain_FirstChanceException);


    private void CurrentDomain_FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
    {
            Dispatcher.BeginInvoke(new Action(() => MessageBox.Show("Error Occurred nr"   e.Exception.Message   "nr"   e.Exception.StackTrace, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error)));
    }
 

Комментарии:

1. Я не уверен, что это то, что вы хотите корректно обрабатывать исключения ViewModel.

2. Вы правы. Это работает, но после нескольких месяцев работы с ним я нахожу, что это не лучший способ обработки ошибок ViewModel, на мой взгляд. Однако я не нашел другого лучшего решения, возможно, вы знаете какой-либо другой способ обработки исключений ViewModel?

3. Я должен признать, что я все еще не совсем уверен, чего вы пытаетесь достичь. «Обработка» исключений глобально редко является хорошей идеей в любой программе, но на самом деле кажется, что вы пытаетесь управлять исключениями, возникающими во время привязки к ViewModel, что, как я полагаю, означает средства получения и установки свойств. Если это так, стандартный совет будет следующим: получатели никогда не должны выдавать, а сбоями установщика можно управлять (до некоторой степени) с помощью таких механизмов, как IDataErrorInfo. В любом случае ваша ViewModel (локально) должна понимать, что может пойти не так, и справиться с этим, если сможет. У вас есть какой-нибудь конкретный пример кода?

4. Спасибо за ценный вклад. Я согласен с вами, я должен перехватывать потенциальные исключения ViewModel и обрабатывать их соответствующим образом. Честно говоря, я хотел сэкономить время во время разработки, и вместо того, чтобы иметь дело с потенциальными ошибками в получателях и установщиках, я решил полагаться на глобальное исключение, которое уведомит меня, чтобы исправить непредвиденные ошибки. Это внутреннее приложение, поэтому не так уж и важно, как только я устраню все эти ошибки, исключение никогда не должно создаваться снова. Я всегда использую глобальную обработку исключений как последний шанс разобраться с ошибками и уведомить разработчика и пользователя.

Ответ №2:

Вы могли бы обернуть каждый метод в lamba. Что-то вроде этого…

         public async void DoSomething()
        {
            await RunSafe(async () =>
            {
                await model.DoSomething();
                await model.DoSomethingElse();
                await model.DoLastThing();
            });
        }
        private async Task RunSafe(Func<Task> del, [CallerMemberName] String methodName = "")
        {
            try
            {
                Log.Info("Executing {0}", methodName);
                await del();
            }
            catch (Exception ex)
            {
                StatusMessage = string.Format("Error in {0}(...): {1}rn{2}", methodName, ex.Message, ex.ToString());                

                Log.Error("Error occured in plug in.", ex);
            }
        }
 

Ответ №3:

Посмотрите на получение DefaultTraceListener класса. Я видел, как люди извлекали BindingListener из этого свои собственные и переопределяли WriteLine метод для создания собственных исключений.

Вы можете просто запустить одно из них во время запуска вашего приложения, и оно должно работать само по себе:

 public class BindingListener : DefaultTraceListener`
{
    public BindingListener()
    {
        PresentationTraceSources.Refresh();
        PresentationTraceSources.DataBindingSource.Listeners.Add(this);SourceLevels.Error;
    }
    public override void WriteLine(string message){...}
}
 

Примечание: это может не делать именно то, что вы хотите из коробки, возможно, вам придется изменить несколько реквизитов.

Комментарии:

1. Итак, я смог реализовать DefaultTraceListener, и он отлично работает, пока я нахожусь в режиме отладки, но когда я запускаю приложение в release, ошибки привязки не перехватываются. Есть ли способ переопределить это, чтобы DefaultTraceListener по-прежнему работал в режиме выпуска; и как насчет производительности, она тоже будет медленнее, верно?

2. Что касается производительности, вам нужно будет ее протестировать, но я не могу представить, что какая-либо разница в производительности будет значительной. Что касается режима выпуска, вы можете попробовать это : PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.All . Однако я не уверен, действительно ли WPF вызывает ошибки привязки до уровня, который вы можете увидеть в сборке выпуска.