只要十步,你就可以应用表达式树来优化动态调用(二)( 八 )

GetValidator(Type type){var re = ValidateFunc.GetOrAdd(type, CreateValidator);return re;}}}}代码要点:

  1. IValidatorFactory 模型验证器工厂 , 表示创建特定类型的验证器委托
  2. IPropertyValidatorFactory 具体属性的验证表达式创建工厂 , 可以根据规则的增加 , 追加新的实现 。
  3. 使用 Autofac 进行模块管理 。
可以通过《在 C# 中使用依赖注入》来了解如何添加一些细节 。
随堂小练别走!您还有作业 。
以下有一个按照难度分级的需求 , 开发者可以尝试完成这些任务 , 进一步理解和使用本样例中的代码 。
增加一个验证 string max length 的规则难度:D
思路:
和 min length 类似 , 别忘记注册就行 。
增加一个验证 int 必须大于等于 0 的规则难度:D
思路:
只是多了一个新的属性类型 , 别忘记注册就行 。
增加一个 IEnumerable 对象必须包含至少一个元素的规则难度:C
思路:
可以用 Linq 中的 Any 方法来验证
增加一个 IEnumerable 必须已经 ToList 或者 ToArray , 类比 mvc 中的规则难度:C
思路:
其实只要验证是否已经是 ICollection 就可以了 。
支持空对象也输出验证结果难度:C
思路:
如果 input 为空 。 则也要能够输出第一条不满足条件的规则 。 例如 Name Required 。
增加一个验证 int? 必须有值的规则难度:B
思路:
int? 其实是语法糖 , 实际类型是 Nullable 。
增加一个验证枚举必须符合给定的范围难度:B
思路:
枚举是可以被赋值以任意数值范围的 , 例如定义了 Enum TestEnum {None = 0;} 但是 , 强行赋值 233 给这样的属性并不会报错 。 该验证需要验证属性值只能是定义的值 。
也可以增加自己的难度 , 例如支持验证标记为 Flags 的枚举的混合值范围 。
添加一个验证 int A 属性必须和 int B 属性大难度:A
思路:
需要有两个属性参与 。 啥都别管 , 先写一个静态函数来比较两个数值的大小 。 然后在考虑如何表达式化 , 如何柯理化 。 可以参考前面思路 。
额外限定条件 , 不能修改现在接口定义 。
添加一个验证 string A 属性必须和 string B 属性相等 , 忽略大小写难度:A
思路:
和前一个类似 。 但是 , string 的比较比 int 特殊 , 并且需要忽略大小写 。
支持返回全部的验证结果难度:S
思路:
调整验证结果返回值 , 从返回第一个不满足的规则 , 修改为返回所有不满足的规则 , 类比 mvc model state 的效果 。
需要修改组合结果的表达式 , 可以有两种办法 , 一种是内部创建 List 然后将结果放入 , 更为简单的一种是使用 yield return 的方法进行返回 。
需要而外注意的是 , 由于所有规则都运行 , 一些判断就需要进行防御性判断 。 例如在 string 长度判断时 , 需要先判断其是否为空 。 至于 string 为空是否属于满足最小长度要求 , 开发者可以自由决定 , 不是重点 。
支持对象的递归验证难度:SS
思路:
即如果对象包含一个属性又是一个对象 , 则子对象也需要被验证 。
有两种思路:
一是修改 ValidatorFactory 使其支持从 ValidateFunc 中获取验证器作为表达式的一部分 。 该思路需要解决的主要问题是 , ValidateFunc 集合中可能提前不存在子模型的验证器 。 可以使用 Lazy 来解决这个问题 。
二是创建一个 IPropertyValidatorFactory 实现 , 使其能够从 ValidatorFactory 中获取 ValidateFunc 来验证子模型 。 该思路主要要解决的问题是 , 直接实现可能会产生循环依赖 。 可以保存和生成 ValidateFunc 划分在两个接口中 , 解除这种循环依赖 。 该方案较为简单 。


推荐阅读