InventorySample数据验证深度解析:ValidationConstraint与Model绑定校验的完整实现
2026/8/28 16:23:50 网站建设 项目流程

InventorySample数据验证深度解析:ValidationConstraint与Model绑定校验的完整实现

【免费下载链接】InventorySampleSample UWP application for LOB scenarios项目地址: https://gitcode.com/gh_mirrors/in/InventorySample

InventorySample 是一个面向企业级(LOB)场景的 UWP 示例应用,本文带你完整拆解它的数据验证机制:如何通过ValidationConstraint约束类与 MVVM 模式的 Model 绑定校验,在保存前拦截非法数据,保障数据完整性。无论你是 UWP 新手还是 MVVM 实践者,都能在 10 分钟内看懂这套开箱即用的校验体系。

上图是 InventorySample 的客户编辑界面:右侧表单中的每个输入框,在点击 Save 之前都会经过一套约束校验,任何必填项缺失都会被立即拦截并提示。

为什么 UWP 应用必须做数据验证?

任何接受用户输入的应用,都必须先验证数据才能入库,原因有三:

  • 🛡️数据完整性:防止空姓名、非法数量等脏数据写入数据库;
  • 提前失败:在保存前就报错,而不是等到数据库层才抛异常;
  • 🔒业务规则落地:如"折扣不能大于小计金额"这类规则,代码化后不可绕过。

在 MVVM 架构中,验证逻辑应该放在ViewModel层执行、作用于Model数据,并向 View 反馈错误——这正是 InventorySample 的设计思路。

核心接口 IValidationConstraint:一条规则 = 一个判定函数 + 一句错误提示

整个验证体系的地基是 ValidationConstraint.cs 中的接口,它非常简洁:

public interface IValidationConstraint<T> { Func<T, bool> Validate { get; } // 校验函数:输入Model,返回是否通过 string Message { get; } // 失败时的提示文案 }

项目已内置7 个现成的约束类,覆盖绝大多数业务场景:

约束类用途提示文案
RequiredConstraint<T>字段必填属性 '{名称}' 不能为空
RequiredGreaterThanZeroConstraint<T>必填且大于零属性 '{名称}' 不能为空
PositiveConstraint<T>数值必须 ≥ 0属性 '{名称}' 必须为正数
NonZeroConstraint<T>数值不能为零属性 '{名称}' 不能为零
GreaterThanConstraint<T>必须大于指定值属性 '{名称}' 必须大于 {值}
NonGreaterThanConstraint<T>不能超过指定值属性 '{名称}' 不能大于 {值}
LessThanConstraint<T>必须小于指定值属性 '{名称}' 必须小于 {值}

所有约束都是泛型T,同一个约束类可以作用于任意 Model;数值型约束内部用Double.TryParse安全解析,解析失败时不会抛异常,而是按宽松策略放行——对新手很友好。

GenericDetailsViewModel 基类:所有详情页"免费获得"的保存前校验

这套校验不是每个页面各自实现的,而是统一收敛在详情页基类中。如下面的 ViewModel 继承体系图所示,所有*DetailsViewModel都继承自GenericDetailsViewModel<TModel>

在 GenericDetailsViewModel.cs 中,保存命令的流程是:

  1. 用户点击Save→ 触发SaveCommand
  2. 先调用Validate(EditableItem)逐条执行约束,任一失败立即返回Result.Error
  3. 校验通过才执行SaveAsync()真正写库;
  4. 校验失败则弹出对话框:"Validation Error — {错误提示}。请修正后重试"

子 ViewModel 要接入这套机制,只需重写一个虚方法GetValidationConstraints(model),返回约束列表即可——这就是模板方法模式的经典应用。

实战案例:三种实体的真实校验规则

客户:8 个必填字段(RequiredConstraint)

CustomerDetailsViewModel.cs 为CustomerModel声明了姓名、邮箱、地址、城市、地区、邮编、国家共 8 条必填约束。客户详情页就是下图中的编辑面板,任何必填项留空都无法保存:

订单:条件校验(按订单状态动态追加规则)

OrderDetailsViewModel.cs 展示了更高级的用法——条件约束

  • 任何订单都必须指定客户(RequiredGreaterThanZeroConstraint校验CustomerID);
  • 当订单状态 > 0(已提交)时,才追加"付款方式必填";
  • 状态再进一步(> 1,已发货)时,才追加"承运商必填"。

约束列表是动态yield return生成的,所以同一套机制能表达分阶段业务规则,无需 if-else 散落各处。

订单明细:数量与折扣的组合拳

OrderItemDetailsViewModel.cs 是约束组合最丰富的示例,对同一属性叠加多条规则:

  • Quantity(数量):不为零 + 必须为正 + 必须小于 100;
  • Discount(折扣):必须为正 +不能大于 Subtotal(小计)

注意最后一条:NonGreaterThanConstraint的上限值直接取自当前 Model 的model.Subtotal属性,说明约束的边界值不是硬编码的,而是与业务数据实时联动。

Result 结果对象:验证结果与对话框的衔接

校验的返回值统一使用 Result.cs 中的Result类:IsOk标记成败,Message/Description分别承载标题与详情。这种"无异常"的错误传递方式让 ViewModel 逻辑干净、可测试,也与项目中服务层的数据访问风格保持一致。

小结:这套方案好在哪?

优点说明
单一接口一条约束 = 一个函数 + 一句文案,5 分钟即可上手
零重复基类统一执行,每个详情页只写规则、不写流程
泛型复用7 个约束类适用于所有 Model
规则内聚校验逻辑随 ViewModel 定义,随业务演进

📌 关键文件速查:

  • 约束接口与 7 个实现:ValidationConstraint.cs
  • 保存前校验基类:GenericDetailsViewModel.cs
  • 官方验证机制文档:Validation.md
  • 数据模型定义:Models/

理解了IValidationConstraint+GenericDetailsViewModel的组合,你就掌握了 InventorySample 数据验证的完整闭环:定义约束 → 重写方法 → 保存自动校验 → 失败弹窗提示。把这套模式搬进你自己的 MVVM 项目,数据质量就有了第一道坚实防线。

【免费下载链接】InventorySampleSample UWP application for LOB scenarios项目地址: https://gitcode.com/gh_mirrors/in/InventorySample

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询