当前位置: 首页 > news >正文

采用一个自创的验证框架实现对数据实体的验证[扩展篇]

关于“验证框架”,先后推出了《编程篇》、《设计篇》和《改进篇》,本不打算再写《XXX篇》的。但是今天收到两个园友的短消息,想了解一下如何定义自己的验证规则。这实际上涉及到对该“验证框架”的扩展,即如何自定义Validator和对应的ValidatorAttribute与ValidatorElementAttribute。为了让本系列看起来完整,通过《扩展篇》进行收尾。本篇我们写一个简单的Validator,用于验证字符串类型属性成员的长度是否符合要求(实际上我是直接借鉴了EnterLib中VAB下的同名Validator的设计)。

一、创建一个自定义Validator:StringLengthValidator

StringLengthValidator数据实体类型的字符串属性进行校验,确保它的长度符合要求(比如小于或者等于数据库中该列的最大长度)。这是一个非常简单的验证逻辑,只需验证大于(或者大于等于)执行的长度下限,小于(或者小于等于)指定的长度上限就可以了。由于有时候只要求被验证的字符串大(小)于指定的下(上)限,有时候被验证的字符可以包括上(下)限,有时则不可以。为了代表这样的比较方式,我定义如下RangeBoundaryType枚举。Ignore、Iclusive和Exclusive分别表示忽略、包含和不包含指定的上(下)限

   1: public enum RangeBoundaryType
   2: {
   3:     Ignore,
   4:     Inclusive,
   5:     Exclusive
   6: } 

StringLengthValidator的整个定义如下所示,定义在Validate方法中的验证逻辑简单得令人发指,应该无需多做介绍吧。唯一值得一提的是,基于StringLengthValidator的验证消息模板添加了两个占位符{LowerBound}和{UpperBound},最终被被设置的上下限长度所代替。如果你愿意,还可以将{LowerBoundType}和{UpperBoundType}作为占位符。

   1: public class StringLengthValidator: Validator
   2: {
   3:     public int LowerBound { get;  private set; }
   4:     public int UpperBound { get; private set; }
   5:     public RangeBoundaryType LowerBoundType { get; private set; }
   6:     public RangeBoundaryType UpperBoundType { get; private set; }
   7:  
   8:     public StringLengthValidator(string messageTemplate, int lowerBound, RangeBoundaryType lowerBoundType, int upperBound, RangeBoundaryType upperBoundType)
   9:         : base(messageTemplate)
  10:     {
  11:         this.LowerBound = lowerBound;
  12:         this.UpperBound = upperBound;
  13:         this.LowerBoundType = lowerBoundType;
  14:         this.UpperBoundType = upperBoundType;
  15:     }
  16:  
  17:     public override ValidationError Validate(object objectToValidate)
  18:     {
  19:         Guard.ArgumentNotNull(objectToValidate, "objectToValidate");
  20:         if (this.LowerBound > this.UpperBound)
  21:         {
  22:             throw new ArgumentException("UpperBound must not be less than LowerBound!");
  23:         }
  24:         string strValue = objectToValidate as string;
  25:         if (null == strValue)
  26:         {
  27:             throw new ArgumentException("The object to validate must be string!", "objectToValidate");
  28:         }
  29:  
  30:         bool greaterThanLowBound = (this.LowerBoundType == RangeBoundaryType.Ignore) ||
  31:             (strValue.Length > this.LowerBound && this.LowerBoundType == RangeBoundaryType.Exclusive) ||
  32:             (strValue.Length >= this.LowerBound && this.LowerBoundType == RangeBoundaryType.Inclusive);
  33:         bool lessThanUpperBound = (this.UpperBoundType == RangeBoundaryType.Ignore) ||
  34:            (strValue.Length < this.UpperBound && this.UpperBoundType == RangeBoundaryType.Exclusive) ||
  35:            (strValue.Length <= this.UpperBound && this.UpperBoundType == RangeBoundaryType.Inclusive);
  36:  
  37:         if (greaterThanLowBound && lessThanUpperBound)
  38:         {
  39:             return null;
  40:         }
  41:         else
  42:         {
  43:             return this.CreateValidationError(objectToValidate);
  44:         }
  45:     }
  46:  
  47:     public override void FormatMessage(object objectToValidate)
  48:     {
  49:         base.FormatMessage(objectToValidate);
  50:         this.MessageTemplate = this.MessageTemplate.Replace("{LowerBound}", this.LowerBound.ToString())
  51:             .Replace("{UpperBound}", this.UpperBound.ToString());
  52:     }
  53: }

二、为StringLengthValidator创建ValidatorAttribute

自定义的Validator最终通过特性的方式应用到数据实体类型的目标属性上实施验证,所以我们需要为StringLengthValidator定义相应的特性:StringLengthValidatorAttribute。StringLengthValidatorAttribute定义如下,简单起见,我没有在构造函数中指定StringLengthValidator的四个属性,而是让开发者通过属性名称显式地设定。LowerBound、UpperBound、LowerBoundType和UpperBoundType的默认值为Int32.MinValue、Int32.MaxValue、Ignore和Ingore。

   1: public class StringLengthValidatorAttribute : ValidatorAttribute
   2: {
   3:     public int LowerBound { get;  set; }
   4:     public int UpperBound { get;  set; }
   5:     public RangeBoundaryType LowerBoundType { get;  set; }
   6:     public RangeBoundaryType UpperBoundType { get;  set; }
   7:  
   8:     public StringLengthValidatorAttribute(string messageTemplate)
   9:         : base(messageTemplate)
  10:     {
  11:         this.LowerBound = int.MinValue;
  12:         this.UpperBound = int.MaxValue;
  13:         this.LowerBoundType = RangeBoundaryType.Ignore;
  14:         this.UpperBoundType = RangeBoundaryType.Ignore;
  15:     }
  16:  
  17:     public override Validator CreateValidator()
  18:     {
  19:         return new StringLengthValidator(this.MessageTemplate, this.LowerBound, this.LowerBoundType, this.UpperBound, this.UpperBoundType);
  20:     }
  21: }

现在我们将StringLengthValidatorAttribute用于自定义的Foo类型的Bar属性上,定义了4个验证规则要求该属性表示的字符长度:在6到10之间(包含6和10)在6和10之间(不包含6和10)大于6小于10

   1: public class Foo
   2: {
   3:     private const string message4Rule1 = "属性{PropertyName}的长度必须在{LowerBound}(含{LowerBound})与{UpperBound}(含{UpperBound})之间。";
   4:     private const string message4Rule2 = "属性{PropertyName}的长度必须在{LowerBound}(不含{LowerBound})与{UpperBound}(不含{UpperBound})之间。";
   5:     private const string message4Rule3 = "属性{PropertyName}的长度必须大于{LowerBound}。";
   6:     private const string message4Rule4 = "属性{PropertyName}的长度必须小于{UpperBound}。";
   7:  
   8:     [StringLengthValidator(message4Rule1,LowerBound  = 6, LowerBoundType = RangeBoundaryType.Inclusive, UpperBound = 10,UpperBoundType = RangeBoundaryType.Inclusive, RuleName= "rule1")]
   9:     [StringLengthValidator(message4Rule2, LowerBound = 6, LowerBoundType = RangeBoundaryType.Exclusive, UpperBound = 10, UpperBoundType = RangeBoundaryType.Exclusive, RuleName = "rule2")]
  10:     [StringLengthValidator(message4Rule3, LowerBound = 6, LowerBoundType = RangeBoundaryType.Exclusive, RuleName = "rule3")]
  11:     [StringLengthValidator(message4Rule4, LowerBound = 6, UpperBound = 10, UpperBoundType = RangeBoundaryType.Exclusive, RuleName = "rule4")]
  12:     public string Bar { get; set; }
  13: }

现在我们通过如下的静态辅助方法Validate基于指定的验证规则实施验证:

   1: static void Validate<T>(T objectToValidate, string ruleName)
   2: {
   3:     IEnumerable<ValidationError> validationErrors;
   4:     if (!Validation.Validate(objectToValidate,ruleName, out validationErrors))
   5:     {
   6:         Console.WriteLine("\t验证失败:");
   7:         foreach (var error in validationErrors)
   8:         {
   9:             Console.WriteLine("\t\t"+ error.Message);
  10:         }
  11:     }
  12:     else
  13:     {
  14:         Console.WriteLine("\t验证成功!");
  15:     }
  16: }

具体的验证代码如下。根据指定的字符长度上下限(6和10),我们分别将Bar属性的字符长度先后设置成4、6、8、10和12。从执行程序得到的输出可以看出我们的代码执行的验证工作是正确的。

   1: static void Main(string[] args)
   2: {
   3:     var foo = new Foo();
   4:     Console.WriteLine("当前字符长度:{0}", 4);
   5:     foo.Bar = "1234";
   6:     Validate<Foo>(foo, "rule1");
   7:     Validate<Foo>(foo, "rule2");
   8:     Validate<Foo>(foo, "rule3");
   9:     Validate<Foo>(foo, "rule4");
  10:  
  11:     Console.WriteLine("当前字符长度:{0}", 6);
  12:     foo.Bar = "123456";
  13:     Validate<Foo>(foo, "rule1");
  14:     Validate<Foo>(foo, "rule2");
  15:     Validate<Foo>(foo, "rule3");
  16:     Validate<Foo>(foo, "rule4");
  17:  
  18:     Console.WriteLine("当前字符长度:{0}", 8);
  19:     foo.Bar = "12345678";
  20:     Validate<Foo>(foo, "rule1");
  21:     Validate<Foo>(foo, "rule2");
  22:     Validate<Foo>(foo, "rule3");
  23:     Validate<Foo>(foo, "rule4");
  24:  
  25:     Console.WriteLine("当前字符长度:{0}", 10);
  26:     foo.Bar = "1234567890";
  27:     Validate<Foo>(foo, "rule1");
  28:     Validate<Foo>(foo, "rule2");
  29:     Validate<Foo>(foo, "rule3");
  30:     Validate<Foo>(foo, "rule4");
  31:  
  32:     Console.WriteLine("当前字符长度:{0}", 12);
  33:     foo.Bar = "123456789012";
  34:     Validate<Foo>(foo, "rule1");
  35:     Validate<Foo>(foo, "rule2");
  36:     Validate<Foo>(foo, "rule3");
  37:     Validate<Foo>(foo, "rule4");
  38:  
  39: }

 

输出结果:

   1: 当前字符长度:4
   2:         验证失败:
   3:                 属性Bar的长度必须在6(含6)与10(含10)之间。
   4:         验证失败:
   5:                 属性Bar的长度必须在6(不含6)与10(不含10)之间。
   6:         验证失败:
   7:                 属性Bar的长度必须大于6。
   8:         验证成功!
   9: 当前字符长度:6
  10:         验证成功!
  11:         验证失败:
  12:                 属性Bar的长度必须在6(不含6)与10(不含10)之间。
  13:         验证失败:
  14:                 属性Bar的长度必须大于6。
  15:         验证成功!
  16: 当前字符长度:8
  17:         验证成功!
  18:         验证成功!
  19:         验证成功!
  20:         验证成功!
  21: 当前字符长度:10
  22:         验证成功!
  23:         验证失败:
  24:                 属性Bar的长度必须在6(不含6)与10(不含10)之间。
  25:         验证成功!
  26:         验证失败:
  27:                 属性Bar的长度必须小于10。
  28: 当前字符长度:12
  29:         验证失败:
  30:                 属性Bar的长度必须在6(含6)与10(含10)之间。
  31:         验证失败:
  32:                 属性Bar的长度必须在6(不含6)与10(不含10)之间。
  33:         验证成功!
  34:         验证失败:
  35:                 属性Bar的长度必须小于10。

三、为StringLengthValidator创建ValidatorElementAttribute

在这个“验证框架”中,每一个非CompositeValidator不但可以单独实施验证,还可以作为ValidatorElement参与到CompositeValidator中,进行相对复杂的逻辑运算。作为ValidatorElement的Validator同样通过自定义特性的方式应用到数据实体类型的目标属性上,所以我们也需要StringLengthValidator创建相应的ValidatorElementAttribute,即StringLengthValidatorElementAttribute。StringLengthValidatorElementAttribute和StringLengthValidatorAttribute处了MessageTemplate属性替换成Name属性之前,基本相同。

   1: public class StringLengthValidatorElementAttribute : ValidatorElementAttribute
   2: {
   3:     public int LowerBound { get;  set; }
   4:     public int UpperBound { get;  set; }
   5:     public RangeBoundaryType LowerBoundType { get;  set; }
   6:     public RangeBoundaryType UpperBoundType { get;  set; }
   7:  
   8:     public StringLengthValidatorElementAttribute(string name)
   9:         : base(name)
  10:     {
  11:         this.LowerBound = int.MinValue;
  12:         this.UpperBound = int.MaxValue;
  13:         this.LowerBoundType = RangeBoundaryType.Ignore;
  14:         this.UpperBoundType = RangeBoundaryType.Ignore;
  15:     }
  16:  
  17:     public override Validator CreateValidator()
  18:     {
  19:         return new StringLengthValidator(string.Empty, this.LowerBound, this.LowerBoundType, this.UpperBound, this.UpperBoundType) { Name = this.Name };
  20:     }
  21: }

那么,如果我们要求Foo的Bar属性的长度在如下的区间中:[2,4)U(6,10]U[12,14],我们的Foo类型就可以定义成如下的形式:

   1: public class Foo
   2: {
   3:     private const string message4Rule = "属性{PropertyName}的长度必须在如下的区间内:[2,4)U(6,10]U[12,14]。";
   4:  
   5:     [CompositeValidator(message4Rule, "V: between2And4 OR V: between6And10 OR V:between12And14")]
   6:     [StringLengthValidatorElement("between2And4", LowerBound = 2, LowerBoundType = RangeBoundaryType.Inclusive, UpperBound = 4, UpperBoundType = RangeBoundaryType.Exclusive)]
   7:     [StringLengthValidatorElement("between6And10", LowerBound = 6, LowerBoundType = RangeBoundaryType.Exclusive, UpperBound = 10, UpperBoundType = RangeBoundaryType.Inclusive)]
   8:     [StringLengthValidatorElement("between12And14", LowerBound = 12, LowerBoundType = RangeBoundaryType.Inclusive, UpperBound = 14, UpperBoundType = RangeBoundaryType.Inclusive)]
   9:     public string Bar { get; set; }
  10: }

练完收工:)

 

采用一个自创的"验证框架"实现对数据实体的验证[编程篇]
采用一个自创的"验证框架"实现对数据实体的验证[设计篇]
采用一个自创的"验证框架"实现对数据实体的验证[改进篇]
采用一个自创的"验证框架"实现对数据实体的验证[扩展篇]

相关文章:

  • sql查询优化的几个要点
  • Javascript+CSS应用小技巧
  • Active Directory还原工具之二Quest Object Restore for Active Directory
  • Spring3和Quartz2的应用实例
  • Linux 命令一句话
  • 调查:中国互联网公司数据库访问层现状
  • C++程序设计:原理与实践(进阶篇)17.4 使用GUI库
  • xDAIS C++
  • 《PHP和MySQL Web开发从新手到高手(第5版)》一2.6 将数据插入到表中
  • 性能调优工具类MyStopwatch
  • MYSQL5 注射技巧笔记
  • 对于MeeGo你所需了解的......
  • RHEL5.5 更新Firefox
  • Visual C++ MFC 中常用宏的含义
  • 《大数据管理概论》一第1章概  述1.1 大数据的基本概念
  • 2017-09-12 前端日报
  • Angular 响应式表单 基础例子
  • ES6 ...操作符
  • IDEA 插件开发入门教程
  • JAVA SE 6 GC调优笔记
  • Java到底能干嘛?
  • js学习笔记
  • Python爬虫--- 1.3 BS4库的解析器
  • Redis 懒删除(lazy free)简史
  • Vim 折腾记
  • windows下mongoDB的环境配置
  • 大快搜索数据爬虫技术实例安装教学篇
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 汉诺塔算法
  • 精彩代码 vue.js
  • 开源SQL-on-Hadoop系统一览
  • 前端
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 小程序测试方案初探
  • Linux权限管理(week1_day5)--技术流ken
  • 阿里云ACE认证之理解CDN技术
  • 回归生活:清理微信公众号
  • ​ArcGIS Pro 如何批量删除字段
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #100天计划# 2013年9月29日
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (笔试题)分解质因式
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (四)库存超卖案例实战——优化redis分布式锁
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)visual stdio 书签功能介绍
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .net Signalr 使用笔记
  • .net反编译工具
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • @AutoConfigurationPackage的使用
  • [20140403]查询是否产生日志
  • [BT]BUUCTF刷题第4天(3.22)