65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF 示例 – 第十四章 – 验证和异常管理

starIconstarIconstarIconstarIconstarIcon

5.00/5 (10投票s)

2011 年 1 月 7 日

CPOL

10分钟阅读

viewsIcon

127885

使用 IDataErrorInfo 接口结合 Validation 属性在 WPF 中进行验证

Previous Next
第 XIII 章 第 XV 章

系列文章

WCF 示例系列是一系列文章,介绍如何使用 WCF 进行通信和 NHibernate 进行持久化来设计和开发 WPF 客户端。该系列简介描述了文章的范围,并从高层次讨论了架构解决方案。该系列的源代码可在CodePlex找到。

章节概述

本章分为两部分,涵盖 WPF 视图中的验证以及客户端对业务异常和警告的管理。关于验证主题,我们将讨论如何实现从 UI 层到服务器端的全面验证功能。对于警告和异常,应用程序将为客户端异常和服务器方法返回的业务异常提供自定义的异常处理机制;对于业务通知,我们将实现一个美观且流畅的通知功能,该功能使用 Windows 任务栏中的气泡通知消息。

验证 - DTO 实现

到目前为止,我们在系列文章中尚未介绍 WPF 的验证,原因可能是 WPF 验证并不那么直接。我们将描述基于我们的应用程序设计,我们认为全面的解决方案。eDirectory 的解决方案是混合式的,它提供了丰富的 UI 体验,但验证代码不放在业务实体中,而是使用 DTO 类来实现。简而言之,以下是此类验证功能实现的示例:

为了实现上述功能,需要执行以下步骤:

  • DTO 类需要继承自一个新基类:ValidatorDtoBase
  • 为 DTO 属性添加 DataAnnotation 属性以进行验证
  • XAML 绑定需要修改为以下内容:
    1. Mode = TwoWay
    2. ValidatesOnDataErrors = True
    3. Validation.ErrorTemplate 设置为 validationTemplate
  • 命令按钮可以绑定到 DTO 中的 IsValid 属性以启用/禁用

作为 DTO 在上述更改后的示例,CustomerDto 如下所示:

以下是已更改 XAML 的一部分:

最后更改是在 ViewModel 中,以便仅当没有验证错误时才启用“保存”按钮

验证设计讨论

在继续之前,让我们停下来讨论一下我们试图通过上述解决方案实现的目标:

  • 遵循 DRY 原则
  • 易于测试
  • 应能在服务器和客户端执行验证

关于 DRY 原则,理论上,业务实体应负责业务验证。以 eDirectory 的 Customer 类为例,名字字段不能为空,长度不能超过 50 个字符。这是应在实体本身定义的经典验证示例。将此验证放在实体中是可行的,但这会生成一种粗略的验证解决方案,客户端需要调用服务器才能找出是否存在验证错误。在某些项目中,这是可接受的,并且可能是最佳方法,因为它有利于测试和快速开发。

eDirectory 应用程序提出了一种解决方案,可以在 DTO 对象中声明简单的验证,以便 WPF 验证可以在客户端使用,实体也可以在服务器端使用此验证。我们将基于以下文章来构建此解决方案:

基于属性的 WPF MVVM 验证 在本文中,IDataErrorInfo 接口与 ViewModel 类中的验证属性结合使用。我们使用后端逻辑通过 LINQ 查询来检索验证属性和 getter。这个概念最初在WPF 使用属性和 IDataErrorInfo 接口进行 MVVM 验证帖子中介绍。
使用自定义绑定和属性自动验证 WPF 中的业务实体 这是 Sandrino Di Mattia 基于 Phillipp Sumi 的 WPF 自定义绑定工作的一篇有趣文章。在此解决方案中,使用自定义 WPF 绑定进行验证。eDirectory 解决方案非常相似,但它是围绕标准类开发的,无需额外的实现。尽管如此,这还是一篇非常好的文章。
Windows Presentation Foundation 中的验证 这是一篇已有四年但仍然适用的文章,令人惊讶的是,自那时以来事情变化如此之少,也许微软是想告诉我们是时候迁移到强大的 Silverlight 了 Smile
使用 INotifyDataErrorInfo 在 Silverlight 中实现数据验证 本文介绍了在 Silverlight 中使用 INotifyDataErrorInfo。这是一篇好文章,讨论了 IDataErrorInfo 接口的局限性,并介绍了一些控件如何与验证代码交互的内部原理。

在我们继续之前,如果您是那些喜欢 DDD 并发现将验证放在业务实体外部很令人沮丧的人,那么现在可能是时候看看能够更好地解决此类问题的框架了。

IDataErrorInfo 和验证属性实现

eDirectory 解决方案基于WPF 使用属性和 IDataErrorInfo 接口进行 MVVM 验证帖子中提出的想法,并指出:

WPF 通过 IDataErrorInfo 接口为绑定场景提供了验证基础结构。基本上,您必须实现 Item[columnName] 属性,将验证逻辑放在 Model(或 ModelView)中需要验证的每个属性上。从 XAML,您需要设置 ValidatesOnDataErrors 为 true,并决定何时让绑定调用验证逻辑(通过 UpdateSourceTrigger)。然后,想法是通过 System.ComponentModel.DataAnnotations 程序集中的验证属性来泛化 IDataErrorInfo.Item[] 中的验证逻辑。

因此,我们可以创建一个继承自 DtoBase 并实现 IDataErrorInfo 接口的新基类,这样 WPF 控件就可以在发生验证错误时得到通知。基类委托给 DataErrorInfo 的一个实例,该实例负责收集带有验证属性标记的属性,并在执行 IDataErrorInfo 方法时调用验证。

值得注意的是,ValidatorDtoBaseDataErrorInfo 都实现了 IDataErrorinfo 接口,而第一个类在方法被调用时仅仅委托给第二个类。

这个类有一个方面可以在性能上得到改进:您的 DTO 在运行时不会改变,因此在创建验证器和属性映射第一次创建时存储它们是有意义的。这将带来性能提升,尽管您需要注意同步问题和锁定。

Server Side Validation

正如我们在本章开头提到的,我们的目标之一是也能在服务器端检查验证错误。通过提出的解决方案,实现这个要求相对容易。例如,当调用 Customer.Create 方法时,已添加以下代码:

ValidateOperation 方法在基类 EntityBase 中声明,该方法检查 DTO 中的验证错误,如果发现任何错误则抛出业务异常。

XAML 错误模板

WPF 为视图中的验证错误提供了非常基础的支持。但是,通过一些小的更改,我们可以提供更丰富的体验。我们的解决方案提供了三种不同的机制:文本框控件周围的红色边框、验证错误符号和工具提示。

对于红色边框和验证错误符号,创建了一个名为 validationTemplate 的控件模板;对于工具提示,为 TextBox 控件创建了一个自定义样式。这两个声明都已在 App.xaml 文件的 Application.resources 中添加。

要显示工具提示,无需进一步操作。但是,我们需要在每个控件中指示我们想在 XAML 中使用 validationTemplate。您可能更愿意将此模板移到样式中,以下文章对此进行了说明:IDataErrorInfo、错误模板和 WPF

模板和样式的 XAML 如下所示:

业务异常管理

我们在前面的章节中已经介绍了服务器端的业务警告和异常,本章的这一部分将介绍客户端,特别是客户端应用程序如何通知用户这些事件。我们将显示一个模态屏幕,告知用户任何异常,并确保客户端应用程序停止任何处理。然而,对于业务警告,将提供一个通知图标,该图标将使用 Windows 任务栏上的信息气泡来通知用户。

捕获 WPF 应用程序中的所有异常

在 WPF 中捕获所有未处理的异常相对容易。

处理程序实现创建了一个新的 Notifier 视图,并设置了 Handled 属性以指示应用程序已处理异常。

该视图非常简单,左侧为图像,右侧面板用于显示异常详细信息。几个按钮允许将整个异常和调用堆栈复制到剪贴板并关闭窗口。

关于我们之前已经看到过的新视图和控制器,没有什么特别之处。

此时,我们有了一个新的机制来确保所有异常都得到妥善管理。例如,如果在客户端处理过程中发生异常,将显示以下屏幕:

业务异常 - 客户端实现

我们需要利用新的 ExceptionNotifier 来处理从服务器响应中检索到的业务异常。首先,我们需要修改 ServiceAdapterBase 类中的代码,ExecuteCommand 是添加新功能的正确位置。目前该方法如下所示:

我们只需要调用新的 ExceptionNotifier 实现,第一个实现可以是这样的:

我们不需要解释为什么会创建 ExceptionNotifierViewModel 实例,但下一行在实现中至关重要。没有它,客户端在调用服务器时不知道发生了异常,抛出异常可确保客户端处理过程被中断。SuspendProcessException 是必需的,因为接下来将对 Application Dispatcher Unhandled Exception 方法进行以下更改:

依赖注入 - 测试实现

在进行上述更改后,如果我们执行测试,我们会发现所有测试都通过了。事实是我们的覆盖率不够好,我们需要一个新的测试来引起服务器业务异常。“CreateDuplicate”测试正好可以做到这一点。如果执行该测试,将显示以下窗口:

这显然是一个问题。DI 可以帮助解决这个问题。我们需要将业务异常处理封装在一个抽象的服务中,然后提供一个不同的实现,以便在执行测试时使用。我们需要执行以下步骤:

  • 创建一个新接口:IBusinessExceptionManager
  • 修改 ClientServiceLocator 以暴露新接口的实例
  • ServiceAdapterBase ExecuteMethod 中使用新暴露的接口
  • 创建新接口的新实现:BusinessException Manager
  • 在测试项目中添加接口的另一个实现
  • 修改 Spring.Net 配置文件

因此,ServiceAdapterBase 现在看起来像这样:

客户端实现然后是:

测试实现然后是:

现在,如果执行测试,包括新测试在内的所有测试都将通过。

业务警告管理

现在我们有了一个全面的应用程序和业务异常管理机制,但我们还需要找到一种方法来通知业务警告。对于警告使用对话框窗口可能会让用户感到烦恼,因此我们将使用一种在 Windows 任务栏上运行的气泡通知。为此,我们使用了WPF NotifyIcon 项目。提供此功能需要执行以下步骤:

  • 创建一个新接口:IBusinessWarningManager
  • ClientServiceLocator 类中添加一个新服务 WarningManager
  • 更改 ServiceAdapterBase 类以使用新服务
  • 创建新的 BusinessWarningManager
  • 修改 Spring.Net 配置文件以设置 WarningManager

BusinesswarningManager 的实现如下所示:

值得注意的是,在上面的代码中调用 TaskbarIcon 是多么相对容易。然后我们需要更改 ServiceAdapterBase 以调用 BusinessWarningManager 中的新方法。

当客户端收到通知时,Windows 任务栏会自动显示一个通知。

正如我们在异常管理器中所见,我们需要警告管理器的测试实现。

章节总结

WPF 中的验证有时可能很麻烦。在本章中,我们提出了一个使用验证属性的全面解决方案,该方案允许在客户端和服务器端执行相同的验证,从而即使验证逻辑不位于业务实体中也能轻松进行测试。

© . All rights reserved.