【问题标题】:Global exception handling in MVVMMVVM 中的全局异常处理
【发布时间】:2017-02-24 08:58:16
【问题描述】:

有没有办法用 MVVM 模式实现全局异常处理。在我现有的情况下,每当 ViewModel 内部发生错误时,应用程序不会崩溃,只是“隐藏”导致错误的代码之后发生的其余绑定(当然,这对最终用户来说是非常误导的,而不是真的,并且永远不应该那样发生)。我不想为 viewModel 中的每个操作都实现 try catch,而且我不喜欢错误异常的静默方式,我真的很想为 WPF 应用程序实现一种处理全局错误的方式。有没有办法用 MVVM 做到这一点?

【问题讨论】:

  • 当您说“ViewModel 内部发生错误”时,究竟是什么错误?还是您指的是绑定错误(System.Windows.Data 错误)?你怎么知道错误正在发生?
  • 好吧,为了简化您的问题的答案,出于测试目的,我调用 throw new Exception();在我的 ViewModel 绑定操作中
  • 使用 AppDomain.UnhandledException 处理全局异常适用于除 ViewModel 绑定内部发生的错误之外的所有其他情况
  • 当您说“在我的 ViewModel 绑定操作中”时,您的意思是属性 getter 或 setter 正在抛出吗?如果您可以发布一些实际代码,它可能会帮助我们为您提供帮助。

标签: c# wpf mvvm


【解决方案1】:

您可以将每个方法包装在一个 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}\r\n{2}", methodName, ex.Message, ex.ToString());                

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

【讨论】:

    【解决方案2】:

    经过漫长的战斗,我终于找到了一种在 ViewModel 内部实现异常处理的非常简单的方法。虽然创建一个继承自 DefaultTraceListener 的 BindingListener 无疑是在调试模式下查找绑定错误的好方法,但在标准模式下运行解决方案时,这不会捕获 ViewModel 内发生的异常。但 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 \n\r" + e.Exception.Message + "\n\r" + e.Exception.StackTrace, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error)));
        }
    

    【讨论】:

    • 我不确定这是您要优雅地处理 ViewModel 异常的原因。
    • 你是对的。它有效,但在使用它几个月后,我发现它也不是我认为处理 ViewModel 错误的最佳方法。但是,我没有找到其他更好的解决方案,您是否知道其他处理 ViewModel 异常的方法?
    • 我必须承认,我仍然不完全确定您要达到的目标。在任何程序中,全局“处理”异常很少是一个好主意,但实际上您似乎正在尝试管理在 binding 到 ViewModel 期间抛出的异常,我认为这意味着属性获取器和设置器。如果是这样,标准建议是:getter 永远不应该抛出,并且 setter 失败可以(在一定程度上)通过 IDataErrorInfo 等机制进行管理。无论哪种方式,您的 ViewModel(本地)都需要了解可能出现的问题并在可能的情况下处理它。你有一些具体的示例代码吗?
    • 感谢您的宝贵意见。我同意你的看法,我应该捕捉潜在的 ViewModel 异常并相应地处理它们。老实说,我想在开发过程中节省时间,而不是处理 getter 和 setter 中的潜在错误,我决定依赖全局异常来通知我以修复意外错误。它是内部应用程序,所以没什么大不了的,一旦我解决了所有这些错误,就不应该再次抛出异常。我总是将全局异常处理作为处理错误的最后机会,并通知开发人员和用户。
    【解决方案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){...}
    }
    

    注意:这可能无法完全做到你想要的开箱即用,你可能需要修改一些道具。

    【讨论】:

    • 所以我能够实现 DefaultTraceListener,并且在我处于调试模式时效果很好,但是当我在 release 中运行应用程序时,不会捕获绑定错误。有没有办法覆盖它,所以 DefaultTraceListener 仍将在 Release 模式下运行;性能怎么样,也会比较慢吧?
    • 就性能而言,您必须对其进行测试,但我无法想象任何性能差异都会显着。至于发布模式,你可以试试这个:PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.All。但是,我不确定 WPF 是否真的将绑定错误提升到您可以在发布版本中看到的级别。
    猜你喜欢
    • 2016-11-03
    • 2012-06-05
    • 2019-07-18
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 2011-09-29
    相关资源
    最近更新 更多